From 80759ba76056e2deaad5fce61f143739a435f376 Mon Sep 17 00:00:00 2001 From: tmohay Date: Thu, 14 Mar 2019 15:07:06 +1100 Subject: [PATCH 01/11] Maintain a staticnodes.json The staticnodes.json file resides in the pantheon data directory, is parsed at startup to determine which peers can be connected to (aside from bootnodes), and is updated whenever addPeer/removePeer RPC is invoked. This file is not updated when finding neighbour peers (ONLY on RPC call). --- .../jsonrpc/JsonRpcTestMethodsFactory.java | 5 +- .../jsonrpc/JsonRpcMethodsFactory.java | 12 +- .../internal/methods/AdminAddPeer.java | 16 +- .../AbstractEthJsonRpcHttpServiceTest.java | 4 +- .../JsonRpcHttpServiceHostWhitelistTest.java | 4 +- .../jsonrpc/JsonRpcHttpServiceLoginTest.java | 4 +- .../JsonRpcHttpServiceRpcApisTest.java | 4 +- .../jsonrpc/JsonRpcHttpServiceTest.java | 4 +- .../internal/methods/AdminAddPeerTest.java | 115 +++++-------- .../ethereum/p2p/peers/DefaultPeer.java | 13 ++ .../ethereum/p2p/peers/cache/PeerCache.java | 41 +++++ .../peers/cache/PersistentJsonPeerCache.java | 101 +++++++++++ .../p2p/peers/cache/PersistentPeerCache.java | 44 +++++ .../peers/PersistentJsonPeerCacheTest.java | 159 ++++++++++++++++++ .../p2p/peers/invalid_static_nodes.json | 1 + .../p2p/peers/valid_static_nodes.json | 4 + .../tech/pegasys/pantheon/RunnerBuilder.java | 37 +++- .../pegasys/pantheon/cli/PantheonCommand.java | 18 +- .../tech/pegasys/pantheon/RunnerTest.java | 9 +- .../pantheon/cli/CommandTestAbstract.java | 1 + 20 files changed, 501 insertions(+), 95 deletions(-) create mode 100644 ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/peers/cache/PeerCache.java create mode 100644 ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/peers/cache/PersistentJsonPeerCache.java create mode 100644 ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/peers/cache/PersistentPeerCache.java create mode 100644 ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/peers/PersistentJsonPeerCacheTest.java create mode 100644 ethereum/p2p/src/test/resources/tech/pegasys/pantheon/ethereum/p2p/peers/invalid_static_nodes.json create mode 100644 ethereum/p2p/src/test/resources/tech/pegasys/pantheon/ethereum/p2p/peers/valid_static_nodes.json diff --git a/ethereum/jsonrpc/src/integration-test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcTestMethodsFactory.java b/ethereum/jsonrpc/src/integration-test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcTestMethodsFactory.java index cf331a51f7..f38ef5b7b2 100644 --- a/ethereum/jsonrpc/src/integration-test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcTestMethodsFactory.java +++ b/ethereum/jsonrpc/src/integration-test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcTestMethodsFactory.java @@ -35,6 +35,7 @@ import tech.pegasys.pantheon.ethereum.mainnet.ProtocolSchedule; import tech.pegasys.pantheon.ethereum.mainnet.ProtocolSpec; import tech.pegasys.pantheon.ethereum.p2p.api.P2PNetwork; +import tech.pegasys.pantheon.ethereum.p2p.peers.cache.PeerCache; import tech.pegasys.pantheon.ethereum.permissioning.AccountWhitelistController; import tech.pegasys.pantheon.ethereum.worldstate.WorldStateArchive; import tech.pegasys.pantheon.metrics.MetricsSystem; @@ -85,6 +86,7 @@ public Map methods() { final Optional accountWhitelistController = Optional.of(mock(AccountWhitelistController.class)); final PrivacyParameters privacyParameters = mock(PrivacyParameters.class); + final PeerCache peerCache = mock(PeerCache.class); return new JsonRpcMethodsFactory() .methods( @@ -102,6 +104,7 @@ public Map methods() { new HashSet<>(), accountWhitelistController, RpcApis.DEFAULT_JSON_RPC_APIS, - privacyParameters); + privacyParameters, + peerCache); } } diff --git a/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcMethodsFactory.java b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcMethodsFactory.java index b9e7e8938e..eee97ba47a 100644 --- a/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcMethodsFactory.java +++ b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcMethodsFactory.java @@ -90,6 +90,7 @@ import tech.pegasys.pantheon.ethereum.jsonrpc.internal.results.BlockResultFactory; import tech.pegasys.pantheon.ethereum.mainnet.ProtocolSchedule; import tech.pegasys.pantheon.ethereum.p2p.api.P2PNetwork; +import tech.pegasys.pantheon.ethereum.p2p.peers.cache.PeerCache; import tech.pegasys.pantheon.ethereum.p2p.wire.Capability; import tech.pegasys.pantheon.ethereum.permissioning.AccountWhitelistController; import tech.pegasys.pantheon.ethereum.privacy.PrivateTransactionHandler; @@ -124,7 +125,8 @@ public Map methods( final Collection rpcApis, final FilterManager filterManager, final Optional accountsWhitelistController, - final PrivacyParameters privacyParameters) { + final PrivacyParameters privacyParameters, + final PeerCache peerCache) { final BlockchainQueries blockchainQueries = new BlockchainQueries(blockchain, worldStateArchive); return methods( @@ -142,7 +144,8 @@ public Map methods( supportedCapabilities, accountsWhitelistController, rpcApis, - privacyParameters); + privacyParameters, + peerCache); } public Map methods( @@ -160,7 +163,8 @@ public Map methods( final Set supportedCapabilities, final Optional accountsWhitelistController, final Collection rpcApis, - final PrivacyParameters privacyParameters) { + final PrivacyParameters privacyParameters, + final PeerCache peerCache) { final Map enabledMethods = new HashMap<>(); if (!rpcApis.isEmpty()) { addMethods(enabledMethods, new RpcModules(rpcApis)); @@ -267,7 +271,7 @@ blockchainQueries, new TransactionTracer(blockReplay), parameter), if (rpcApis.contains(RpcApis.ADMIN)) { addMethods( enabledMethods, - new AdminAddPeer(p2pNetwork, parameter), + new AdminAddPeer(p2pNetwork, parameter, peerCache), new AdminNodeInfo( clientVersion, networkId, genesisConfigOptions, p2pNetwork, blockchainQueries), new AdminPeers(p2pNetwork)); diff --git a/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/AdminAddPeer.java b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/AdminAddPeer.java index ea9e6d635a..df8a73ffd3 100644 --- a/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/AdminAddPeer.java +++ b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/AdminAddPeer.java @@ -24,18 +24,24 @@ import tech.pegasys.pantheon.ethereum.p2p.api.P2PNetwork; import tech.pegasys.pantheon.ethereum.p2p.peers.DefaultPeer; import tech.pegasys.pantheon.ethereum.p2p.peers.Peer; +import tech.pegasys.pantheon.ethereum.p2p.peers.cache.PeerCache; +import tech.pegasys.pantheon.util.enode.EnodeURL; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; public class AdminAddPeer implements JsonRpcMethod { + private static final Logger LOG = LogManager.getLogger(); private final P2PNetwork peerNetwork; private final JsonRpcParameter parameters; + private final PeerCache peerCache; - public AdminAddPeer(final P2PNetwork peerNetwork, final JsonRpcParameter parameters) { + public AdminAddPeer( + final P2PNetwork peerNetwork, final JsonRpcParameter parameters, final PeerCache peerCache) { this.peerNetwork = peerNetwork; this.parameters = parameters; + this.peerCache = peerCache; } @Override @@ -50,9 +56,11 @@ public JsonRpcResponse response(final JsonRpcRequest req) { } try { final String enodeString = parameters.required(req.getParams(), 0, String.class); - final Peer peer = DefaultPeer.fromURI(enodeString); - final boolean added = peerNetwork.addMaintainConnectionPeer(peer); - return new JsonRpcSuccessResponse(req.getId(), added); + final EnodeURL enodeURL = new EnodeURL(enodeString); + final Peer peer = DefaultPeer.fromEnodeURL(enodeURL); + boolean addedToNetwork = peerNetwork.addMaintainConnectionPeer(peer); + boolean addedToCache = peerCache.add(enodeURL); + return new JsonRpcSuccessResponse(req.getId(), addedToNetwork && addedToCache); } catch (final InvalidJsonRpcParameters e) { return new JsonRpcErrorResponse(req.getId(), JsonRpcError.INVALID_PARAMS); } catch (final IllegalArgumentException e) { diff --git a/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/AbstractEthJsonRpcHttpServiceTest.java b/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/AbstractEthJsonRpcHttpServiceTest.java index 37264f01fc..6b12e5bb7b 100644 --- a/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/AbstractEthJsonRpcHttpServiceTest.java +++ b/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/AbstractEthJsonRpcHttpServiceTest.java @@ -45,6 +45,7 @@ import tech.pegasys.pantheon.ethereum.mainnet.ProtocolSpec; import tech.pegasys.pantheon.ethereum.mainnet.ValidationResult; import tech.pegasys.pantheon.ethereum.p2p.api.P2PNetwork; +import tech.pegasys.pantheon.ethereum.p2p.peers.cache.PeerCache; import tech.pegasys.pantheon.ethereum.p2p.wire.Capability; import tech.pegasys.pantheon.ethereum.util.RawBlockIterator; import tech.pegasys.pantheon.ethereum.worldstate.WorldStateArchive; @@ -187,7 +188,8 @@ public void setupTest() throws Exception { supportedCapabilities, Optional.empty(), JSON_RPC_APIS, - privacyParameters); + privacyParameters, + mock(PeerCache.class)); final JsonRpcConfiguration config = JsonRpcConfiguration.createDefault(); config.setPort(0); service = diff --git a/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcHttpServiceHostWhitelistTest.java b/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcHttpServiceHostWhitelistTest.java index 26c2e3752a..5f6cffd53b 100644 --- a/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcHttpServiceHostWhitelistTest.java +++ b/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcHttpServiceHostWhitelistTest.java @@ -27,6 +27,7 @@ import tech.pegasys.pantheon.ethereum.jsonrpc.internal.queries.BlockchainQueries; import tech.pegasys.pantheon.ethereum.mainnet.MainnetProtocolSchedule; import tech.pegasys.pantheon.ethereum.p2p.api.P2PNetwork; +import tech.pegasys.pantheon.ethereum.p2p.peers.cache.PeerCache; import tech.pegasys.pantheon.ethereum.p2p.wire.Capability; import tech.pegasys.pantheon.ethereum.permissioning.AccountWhitelistController; import tech.pegasys.pantheon.metrics.noop.NoOpMetricsSystem; @@ -101,7 +102,8 @@ public void initServerAndClient() throws Exception { supportedCapabilities, Optional.of(mock(AccountWhitelistController.class)), JSON_RPC_APIS, - mock(PrivacyParameters.class))); + mock(PrivacyParameters.class), + mock(PeerCache.class))); service = createJsonRpcHttpService(); service.start().join(); diff --git a/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcHttpServiceLoginTest.java b/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcHttpServiceLoginTest.java index abd3d94b8e..f6264c4816 100644 --- a/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcHttpServiceLoginTest.java +++ b/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcHttpServiceLoginTest.java @@ -33,6 +33,7 @@ import tech.pegasys.pantheon.ethereum.jsonrpc.internal.queries.BlockchainQueries; import tech.pegasys.pantheon.ethereum.mainnet.MainnetProtocolSchedule; import tech.pegasys.pantheon.ethereum.p2p.api.P2PNetwork; +import tech.pegasys.pantheon.ethereum.p2p.peers.cache.PeerCache; import tech.pegasys.pantheon.ethereum.p2p.wire.Capability; import tech.pegasys.pantheon.metrics.noop.NoOpMetricsSystem; @@ -132,7 +133,8 @@ public static void initServerAndClient() throws Exception { supportedCapabilities, Optional.empty(), JSON_RPC_APIS, - mock(PrivacyParameters.class))); + mock(PrivacyParameters.class), + mock(PeerCache.class))); service = createJsonRpcHttpService(); jwtAuth = service.authenticationService.get().getJwtAuthProvider(); service.start().join(); diff --git a/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcHttpServiceRpcApisTest.java b/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcHttpServiceRpcApisTest.java index eb10d8e40e..e4814ce44e 100644 --- a/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcHttpServiceRpcApisTest.java +++ b/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcHttpServiceRpcApisTest.java @@ -29,6 +29,7 @@ import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcError; import tech.pegasys.pantheon.ethereum.mainnet.MainnetProtocolSchedule; import tech.pegasys.pantheon.ethereum.p2p.api.P2PNetwork; +import tech.pegasys.pantheon.ethereum.p2p.peers.cache.PeerCache; import tech.pegasys.pantheon.ethereum.p2p.wire.Capability; import tech.pegasys.pantheon.ethereum.permissioning.AccountWhitelistController; import tech.pegasys.pantheon.metrics.noop.NoOpMetricsSystem; @@ -184,7 +185,8 @@ private JsonRpcHttpService createJsonRpcHttpServiceWithRpcApis(final JsonRpcConf supportedCapabilities, Optional.of(mock(AccountWhitelistController.class)), config.getRpcApis(), - mock(PrivacyParameters.class))); + mock(PrivacyParameters.class), + mock(PeerCache.class))); final JsonRpcHttpService jsonRpcHttpService = new JsonRpcHttpService( vertx, folder.newFolder().toPath(), config, new NoOpMetricsSystem(), rpcMethods); diff --git a/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcHttpServiceTest.java b/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcHttpServiceTest.java index b4d7431c5e..fb8377e5e2 100644 --- a/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcHttpServiceTest.java +++ b/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcHttpServiceTest.java @@ -42,6 +42,7 @@ import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcError; import tech.pegasys.pantheon.ethereum.mainnet.MainnetProtocolSchedule; import tech.pegasys.pantheon.ethereum.p2p.api.P2PNetwork; +import tech.pegasys.pantheon.ethereum.p2p.peers.cache.PeerCache; import tech.pegasys.pantheon.ethereum.p2p.wire.Capability; import tech.pegasys.pantheon.ethereum.permissioning.AccountWhitelistController; import tech.pegasys.pantheon.metrics.noop.NoOpMetricsSystem; @@ -129,7 +130,8 @@ public static void initServerAndClient() throws Exception { supportedCapabilities, Optional.of(mock(AccountWhitelistController.class)), JSON_RPC_APIS, - mock(PrivacyParameters.class))); + mock(PrivacyParameters.class), + mock(PeerCache.class))); service = createJsonRpcHttpService(); service.start().join(); diff --git a/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/AdminAddPeerTest.java b/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/AdminAddPeerTest.java index bbe316380d..e9bde201b4 100644 --- a/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/AdminAddPeerTest.java +++ b/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/AdminAddPeerTest.java @@ -14,8 +14,11 @@ 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.verify; import static org.mockito.Mockito.when; +import tech.pegasys.pantheon.crypto.SECP256K1.PublicKey; import tech.pegasys.pantheon.ethereum.jsonrpc.internal.JsonRpcRequest; import tech.pegasys.pantheon.ethereum.jsonrpc.internal.parameters.JsonRpcParameter; import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcError; @@ -25,6 +28,10 @@ import tech.pegasys.pantheon.ethereum.p2p.P2pDisabledException; import tech.pegasys.pantheon.ethereum.p2p.PeerNotWhitelistedException; import tech.pegasys.pantheon.ethereum.p2p.api.P2PNetwork; +import tech.pegasys.pantheon.ethereum.p2p.peers.cache.PeerCache; +import tech.pegasys.pantheon.util.enode.EnodeURL; + +import java.math.BigInteger; import org.junit.Before; import org.junit.Test; @@ -36,13 +43,23 @@ public class AdminAddPeerTest { @Mock private P2PNetwork p2pNetwork; + @Mock private PeerCache peerCache; private final JsonRpcParameter parameter = new JsonRpcParameter(); private AdminAddPeer method; + private final EnodeURL validEnodeURL = + new EnodeURL( + PublicKey.create(BigInteger.valueOf(0)).toString().substring(2), "127.0.0.1", 30303); + + private final JsonRpcRequest validRequest = + new JsonRpcRequest("2.0", "admin_addPeer", new String[] {validEnodeURL.toString()}); + @Before public void setup() { - method = new AdminAddPeer(p2pNetwork, parameter); + + method = new AdminAddPeer(p2pNetwork, parameter, peerCache); + when(peerCache.add(any())).thenReturn(true); } @Test @@ -94,46 +111,38 @@ public void requestHasInvalidEnode() { public void requestAddsValidEnode() { when(p2pNetwork.addMaintainConnectionPeer(any())).thenReturn(true); + final JsonRpcResponse expectedResponse = new JsonRpcSuccessResponse(validRequest.getId(), true); + + final JsonRpcResponse actualResponse = method.response(validRequest); + + assertThat(actualResponse).isEqualToComparingFieldByField(expectedResponse); + verify(peerCache).add(eq(validEnodeURL)); + } + + @Test + public void ifPeerIsNotAddedToCacheFalseIsReturned() { + when(p2pNetwork.addMaintainConnectionPeer(any())).thenReturn(true); + when(peerCache.add(any())).thenReturn(false); + final JsonRpcRequest request = - new JsonRpcRequest( - "2.0", - "admin_addPeer", - new String[] { - "enode://" - + "00000000000000000000000000000000" - + "00000000000000000000000000000000" - + "00000000000000000000000000000000" - + "00000000000000000000000000000000" - + "@127.0.0.1:30303" - }); + new JsonRpcRequest("2.0", "admin_addPeer", new String[] {validEnodeURL.toString()}); - final JsonRpcResponse expectedResponse = new JsonRpcSuccessResponse(request.getId(), true); + final JsonRpcResponse expectedResponse = new JsonRpcSuccessResponse(request.getId(), false); final JsonRpcResponse actualResponse = method.response(request); assertThat(actualResponse).isEqualToComparingFieldByField(expectedResponse); + verify(peerCache).add(eq(validEnodeURL)); } @Test public void requestRefusesListOfNodes() { + final JsonRpcRequest request = new JsonRpcRequest( "2.0", "admin_addPeer", - new String[] { - "enode://" - + "00000000000000000000000000000000" - + "00000000000000000000000000000000" - + "00000000000000000000000000000000" - + "00000000000000000000000000000000" - + "@127.0.0.1:30303", - "enode://" - + "00000000000000000000000000000000" - + "00000000000000000000000000000000" - + "00000000000000000000000000000000" - + "00000000000000000000000000000001" - + "@127.0.0.2:30303" - }); + new String[] {validEnodeURL.toString(), validEnodeURL.toString()}); final JsonRpcResponse expectedResponse = new JsonRpcErrorResponse(request.getId(), JsonRpcError.INVALID_PARAMS); @@ -147,22 +156,10 @@ public void requestRefusesListOfNodes() { public void requestReturnsFalseIfAddFails() { when(p2pNetwork.addMaintainConnectionPeer(any())).thenReturn(false); - final JsonRpcRequest request = - new JsonRpcRequest( - "2.0", - "admin_addPeer", - new String[] { - "enode://" - + "00000000000000000000000000000000" - + "00000000000000000000000000000000" - + "00000000000000000000000000000000" - + "00000000000000000000000000000000" - + "@127.0.0.1:30303" - }); + final JsonRpcResponse expectedResponse = + new JsonRpcSuccessResponse(validRequest.getId(), false); - final JsonRpcResponse expectedResponse = new JsonRpcSuccessResponse(request.getId(), false); - - final JsonRpcResponse actualResponse = method.response(request); + final JsonRpcResponse actualResponse = method.response(validRequest); assertThat(actualResponse).isEqualToComparingFieldByField(expectedResponse); } @@ -173,23 +170,10 @@ public void requestReturnsErrorWhenP2pDisabled() { .thenThrow( new P2pDisabledException("P2P networking disabled. Unable to connect to add peer.")); - final JsonRpcRequest request = - new JsonRpcRequest( - "2.0", - "admin_addPeer", - new String[] { - "enode://" - + "00000000000000000000000000000000" - + "00000000000000000000000000000000" - + "00000000000000000000000000000000" - + "00000000000000000000000000000000" - + "@127.0.0.1:30303" - }); - final JsonRpcResponse expectedResponse = - new JsonRpcErrorResponse(request.getId(), JsonRpcError.P2P_DISABLED); + new JsonRpcErrorResponse(validRequest.getId(), JsonRpcError.P2P_DISABLED); - final JsonRpcResponse actualResponse = method.response(request); + final JsonRpcResponse actualResponse = method.response(validRequest); assertThat(actualResponse).isEqualToComparingFieldByField(expectedResponse); } @@ -199,24 +183,11 @@ public void requestReturnsErrorWhenPeerNotWhitelisted() { when(p2pNetwork.addMaintainConnectionPeer(any())) .thenThrow(new PeerNotWhitelistedException("Cannot add peer that is not whitelisted")); - final JsonRpcRequest request = - new JsonRpcRequest( - "2.0", - "admin_addPeer", - new String[] { - "enode://" - + "00000000000000000000000000000000" - + "00000000000000000000000000000000" - + "00000000000000000000000000000000" - + "00000000000000000000000000000000" - + "@127.0.0.1:30303" - }); - final JsonRpcResponse expectedResponse = new JsonRpcErrorResponse( - request.getId(), JsonRpcError.NON_WHITELISTED_NODE_CANNOT_BE_ADDED_AS_A_PEER); + validRequest.getId(), JsonRpcError.NON_WHITELISTED_NODE_CANNOT_BE_ADDED_AS_A_PEER); - final JsonRpcResponse actualResponse = method.response(request); + final JsonRpcResponse actualResponse = method.response(validRequest); assertThat(actualResponse).isEqualToComparingFieldByField(expectedResponse); } diff --git a/ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/peers/DefaultPeer.java b/ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/peers/DefaultPeer.java index b1c94d19aa..0ba705b673 100644 --- a/ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/peers/DefaultPeer.java +++ b/ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/peers/DefaultPeer.java @@ -20,6 +20,7 @@ import tech.pegasys.pantheon.ethereum.rlp.RLPInput; import tech.pegasys.pantheon.util.NetworkUtility; import tech.pegasys.pantheon.util.bytes.BytesValue; +import tech.pegasys.pantheon.util.enode.EnodeURL; import java.net.URI; import java.util.Objects; @@ -42,6 +43,18 @@ public class DefaultPeer extends DefaultPeerId implements Peer { private final Endpoint endpoint; + public static DefaultPeer fromEnodeURL(final EnodeURL enodeURL) { + final int udpPort = enodeURL.getDiscoveryPort().orElse(enodeURL.getListeningPort()); + + final Endpoint endpoint = + new Endpoint( + enodeURL.getInetAddress().getHostAddress(), + udpPort, + OptionalInt.of(enodeURL.getListeningPort())); + + return new DefaultPeer(BytesValue.fromHexString(enodeURL.getNodeId()), endpoint); + } + /** * Creates a {@link DefaultPeer} instance from a String representation of an enode URL. * diff --git a/ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/peers/cache/PeerCache.java b/ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/peers/cache/PeerCache.java new file mode 100644 index 0000000000..070648516a --- /dev/null +++ b/ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/peers/cache/PeerCache.java @@ -0,0 +1,41 @@ +/* + * Copyright 2019 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.peers.cache; + +import tech.pegasys.pantheon.util.enode.EnodeURL; + +import java.util.HashSet; +import java.util.Set; + +import com.google.common.collect.ImmutableList; + +public class PeerCache { + + private final Set nodes = new HashSet<>(); + + public PeerCache(final Set initialNodes) { + nodes.addAll(initialNodes); + } + + public boolean add(final EnodeURL enode) throws IllegalArgumentException { + return nodes.add(enode); + } + + public boolean remove(final EnodeURL enode) { + return nodes.remove(enode); + } + + public ImmutableList getStaticNodes() { + return ImmutableList.copyOf(nodes); + } +} diff --git a/ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/peers/cache/PersistentJsonPeerCache.java b/ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/peers/cache/PersistentJsonPeerCache.java new file mode 100644 index 0000000000..33ada81cec --- /dev/null +++ b/ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/peers/cache/PersistentJsonPeerCache.java @@ -0,0 +1,101 @@ +/* + * Copyright 2019 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.peers.cache; + +import tech.pegasys.pantheon.util.enode.EnodeURL; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.NoSuchFileException; +import java.nio.file.Path; +import java.util.HashSet; +import java.util.Set; +import java.util.stream.Collectors; + +import io.vertx.core.json.DecodeException; +import io.vertx.core.json.JsonArray; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class PersistentJsonPeerCache extends PersistentPeerCache { + + private static final Logger LOG = LogManager.getLogger(); + private static final Charset UTF_8 = Charset.forName("UTF-8"); + + private final Path path; + + public PersistentJsonPeerCache(final Set initialNodes, final Path path) { + super(initialNodes); + this.path = path; + } + + public static PersistentJsonPeerCache fromPath(final Path path) + throws IOException, IllegalArgumentException { + final Set result = new HashSet<>(); + + final byte[] staticNodesContent; + try { + staticNodesContent = Files.readAllBytes(path); + if (staticNodesContent.length == 0) { + return new PersistentJsonPeerCache(result, path); + } + } catch (FileNotFoundException | NoSuchFileException ex) { + LOG.info("No StaticNodes file ({}) exists, creating empty cache.", path); + return new PersistentJsonPeerCache(result, path); + } catch (IOException ex) { + LOG.info("Unable to parse static nodes file ({})", path); + throw ex; + } + + try { + final JsonArray enodeJsonArray = new JsonArray(new String(staticNodesContent, UTF_8)); + for (Object jsonObj : enodeJsonArray.getList()) { + final String enodeString = (String) jsonObj; + result.add(decodeString(enodeString)); + } + return new PersistentJsonPeerCache(result, path); + } catch (DecodeException ex) { + LOG.info("Content of ({}} was invalid json, and could not be decoded.", path); + throw ex; + } catch (IllegalArgumentException ex) { + LOG.info("Parsing ({}) has failed due incorrectly formatted enode element.", path); + throw ex; + } + } + + private static EnodeURL decodeString(final String input) { + try { + return new EnodeURL(input); + } catch (IllegalArgumentException ex) { + LOG.info("Illegally constructed enode supplied ({})", input); + throw ex; + } + } + + // TODO: This should potentially be using an atomicMove to prevent file corruption + // on sudden terminations. + @Override + protected void persist() { + final JsonArray array = + new JsonArray( + getStaticNodes().stream().map(EnodeURL::toString).collect(Collectors.toList())); + + try { + Files.write(path, array.toString().getBytes(UTF_8)); + } catch (IOException ex) { + LOG.info("Unable to persist peers to disk, file write failed."); + } + } +} diff --git a/ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/peers/cache/PersistentPeerCache.java b/ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/peers/cache/PersistentPeerCache.java new file mode 100644 index 0000000000..befae4c800 --- /dev/null +++ b/ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/peers/cache/PersistentPeerCache.java @@ -0,0 +1,44 @@ +/* + * Copyright 2019 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.peers.cache; + +import tech.pegasys.pantheon.util.enode.EnodeURL; + +import java.util.Set; + +public abstract class PersistentPeerCache extends PeerCache { + + public PersistentPeerCache(final Set initialNodes) { + super(initialNodes); + } + + @Override + public boolean add(final EnodeURL enode) throws IllegalArgumentException { + boolean cachedNodesAltered = super.add(enode); + if (cachedNodesAltered) { + persist(); + } + return cachedNodesAltered; + } + + @Override + public boolean remove(final EnodeURL enode) { + boolean cachedNodesAltered = super.remove(enode); + if (cachedNodesAltered) { + persist(); + } + return cachedNodesAltered; + } + + protected abstract void persist(); +} diff --git a/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/peers/PersistentJsonPeerCacheTest.java b/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/peers/PersistentJsonPeerCacheTest.java new file mode 100644 index 0000000000..fbe4eaa80a --- /dev/null +++ b/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/peers/PersistentJsonPeerCacheTest.java @@ -0,0 +1,159 @@ +/* + * Copyright 2019 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.peers; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import tech.pegasys.pantheon.ethereum.p2p.peers.cache.PeerCache; +import tech.pegasys.pantheon.ethereum.p2p.peers.cache.PersistentJsonPeerCache; +import tech.pegasys.pantheon.util.enode.EnodeURL; + +import java.io.File; +import java.io.IOException; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; + +import com.google.common.collect.ImmutableList; +import io.vertx.core.json.DecodeException; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +public class PersistentJsonPeerCacheTest { + + // NOTE: The invalid_static_nodes file is identical to the valid, however one node's port is set + // to "A". + + // First peer ion the valid_static_nodes file. + private final EnodeURL firstItemInValidList = + new EnodeURL( + "50203c6bfca6874370e71aecc8958529fd723feb05013dc1abca8fc1fff845c5259faba05852e9dfe5ce172a7d6e7c2a3a5eaa8b541c8af15ea5518bbff5f2fa", + "127.0.0.1", + 30303); + + @Rule public TemporaryFolder testFolder = new TemporaryFolder(); + + @Test + public void validFileLoadsWithExpectedNumberOfPeers() throws IOException { + final URL resource = PersistentJsonPeerCacheTest.class.getResource("valid_static_nodes.json"); + final Path path = Paths.get(resource.getPath()); + + final PeerCache cache = PersistentJsonPeerCache.fromPath(path); + final ImmutableList enodes = cache.getStaticNodes(); + + assertThat(enodes.size()).isEqualTo(4); + } + + @Test + public void invalidFileThrowsAnException() { + final URL resource = PersistentJsonPeerCacheTest.class.getResource("invalid_static_nodes.json"); + final Path path = Paths.get(resource.getPath()); + + assertThatThrownBy(() -> PersistentJsonPeerCache.fromPath(path)) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + public void nonJsonFileThrowsAnException() throws IOException { + final File tempFile = testFolder.newFile("file.txt"); + tempFile.deleteOnExit(); + Files.write(tempFile.toPath(), "This Is Not Json".getBytes(Charset.forName("UTF-8"))); + + assertThatThrownBy(() -> PersistentJsonPeerCache.fromPath(tempFile.toPath())) + .isInstanceOf(DecodeException.class); + } + + @Test + public void anEmptyCacheIsCreatedIfTheFileDoesNotExist() throws IOException { + final Path path = Paths.get("./arbirtraryFilename.txt"); + + final PeerCache cache = PersistentJsonPeerCache.fromPath(path); + assertThat(cache.getStaticNodes().size()).isZero(); + } + + @Test + public void cacheIsCreatedIfFileExistsButIsEmpty() throws IOException { + final File tempFile = testFolder.newFile("file.txt"); + tempFile.deleteOnExit(); + + final PeerCache cache = PersistentJsonPeerCache.fromPath(tempFile.toPath()); + final ImmutableList enodes = cache.getStaticNodes(); + + assertThat(enodes.size()).isEqualTo(0); + } + + @Test + public void addPeerReturnsFalseIfSuppliedPeerIsAlreadyInList() throws IOException { + final URL resource = PersistentJsonPeerCacheTest.class.getResource("valid_static_nodes.json"); + final Path original = Paths.get(resource.getPath()); + final File tempFile = testFolder.newFile("file.txt"); + tempFile.deleteOnExit(); + final Path tempPath = tempFile.toPath(); + Files.copy(original, tempPath, StandardCopyOption.REPLACE_EXISTING); + + final PeerCache cache = PersistentJsonPeerCache.fromPath(tempPath); + + final ImmutableList preAddList = cache.getStaticNodes(); + assertThat(cache.add(firstItemInValidList)).isFalse(); + assertThat(cache.getStaticNodes()).containsExactlyElementsOf(preAddList); + } + + @Test + public void addPeerWritesToFileIfPeerIsNotAlreadyInList() throws IOException { + final File tempFile = testFolder.newFile("file.txt"); + tempFile.deleteOnExit(); + final PeerCache cache = PersistentJsonPeerCache.fromPath(tempFile.toPath()); + + assertThat(cache.add(firstItemInValidList)).isTrue(); + assertThat(cache.getStaticNodes()).containsExactly(firstItemInValidList); + + // Ensure file has been updated by reloading into a new cache (ensures file validity as well). + final PersistentJsonPeerCache newCache = PersistentJsonPeerCache.fromPath(tempFile.toPath()); + assertThat(newCache.getStaticNodes()).contains(firstItemInValidList); + } + + @Test + public void removePeerReturnsFalseIfSuppliedPeerIsNotInList() throws IOException { + final File tempFile = testFolder.newFile("file.txt"); + tempFile.deleteOnExit(); + final PeerCache cache = PersistentJsonPeerCache.fromPath(tempFile.toPath()); + + assertThat(cache.remove(firstItemInValidList)).isFalse(); + assertThat(cache.getStaticNodes().size()).isZero(); + } + + @Test + public void removePeerReturnsTrueAndRemovesItemIfPeerIsInList() + throws IOException { + final URL resource = PersistentJsonPeerCacheTest.class.getResource("valid_static_nodes.json"); + final Path original = Paths.get(resource.getPath()); + final File tempFile = testFolder.newFile("file.txt"); + tempFile.deleteOnExit(); + final Path tempPath = tempFile.toPath(); + Files.copy(original, tempPath, StandardCopyOption.REPLACE_EXISTING); + + final PeerCache cache = PersistentJsonPeerCache.fromPath(tempPath); + assertThat(cache.remove(firstItemInValidList)).isTrue(); + assertThat(cache.getStaticNodes()).doesNotContain(firstItemInValidList); + + // Ensure file has been updated by reloading into a new cache (ensures file validity as well). + final PersistentJsonPeerCache newCache = PersistentJsonPeerCache.fromPath(tempPath); + assertThat(newCache.getStaticNodes()).doesNotContain(firstItemInValidList); + } +} diff --git a/ethereum/p2p/src/test/resources/tech/pegasys/pantheon/ethereum/p2p/peers/invalid_static_nodes.json b/ethereum/p2p/src/test/resources/tech/pegasys/pantheon/ethereum/p2p/peers/invalid_static_nodes.json new file mode 100644 index 0000000000..3b55676f90 --- /dev/null +++ b/ethereum/p2p/src/test/resources/tech/pegasys/pantheon/ethereum/p2p/peers/invalid_static_nodes.json @@ -0,0 +1 @@ +["enode://50203c6bfca6874370e71aecc8958529fd723feb05013dc1abca8fc1fff845c5259faba05852e9dfe5ce172a7d6e7c2a3a5eaa8b541c8af15ea5518bbff5f2fa@127.0.0.1:A","enode://02beb46bc17227616be44234071dfa18516684e45eed88049190b6cb56b0bae218f045fd0450f123b8f55c60b96b78c45e8e478004293a8de6818aa4e02eff97@127.0.0.1:30304","enode://819e5cbd81f123516b10f04bf620daa2b385efef06d77253148b814bf1bb6197ff58ebd1fd7bf5dc765b49a4440c733bf941e479c800173f2bfeb887e4fbcbc2@127.0.0.1:30305","enode://6cf53e25d2a98a22e7e205a86bda7077e3c8a7bc99e5ff88ddfd2037a550969ab566f069ffa455df0cfae0c21f7aec3447e414eccc473a3e8b20984b90f164ac@127.0.0.1:30306"] \ No newline at end of file diff --git a/ethereum/p2p/src/test/resources/tech/pegasys/pantheon/ethereum/p2p/peers/valid_static_nodes.json b/ethereum/p2p/src/test/resources/tech/pegasys/pantheon/ethereum/p2p/peers/valid_static_nodes.json new file mode 100644 index 0000000000..1e1764425d --- /dev/null +++ b/ethereum/p2p/src/test/resources/tech/pegasys/pantheon/ethereum/p2p/peers/valid_static_nodes.json @@ -0,0 +1,4 @@ +["enode://50203c6bfca6874370e71aecc8958529fd723feb05013dc1abca8fc1fff845c5259faba05852e9dfe5ce172a7d6e7c2a3a5eaa8b541c8af15ea5518bbff5f2fa@127.0.0.1:30303", + "enode://02beb46bc17227616be44234071dfa18516684e45eed88049190b6cb56b0bae218f045fd0450f123b8f55c60b96b78c45e8e478004293a8de6818aa4e02eff97@127.0.0.1:30304", + "enode://819e5cbd81f123516b10f04bf620daa2b385efef06d77253148b814bf1bb6197ff58ebd1fd7bf5dc765b49a4440c733bf941e479c800173f2bfeb887e4fbcbc2@127.0.0.1:30305", + "enode://6cf53e25d2a98a22e7e205a86bda7077e3c8a7bc99e5ff88ddfd2037a550969ab566f069ffa455df0cfae0c21f7aec3447e414eccc473a3e8b20984b90f164ac@127.0.0.1:30306"] \ No newline at end of file diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/RunnerBuilder.java b/pantheon/src/main/java/tech/pegasys/pantheon/RunnerBuilder.java index b48943a795..1860ae8055 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/RunnerBuilder.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/RunnerBuilder.java @@ -49,7 +49,10 @@ import tech.pegasys.pantheon.ethereum.p2p.config.RlpxConfiguration; import tech.pegasys.pantheon.ethereum.p2p.config.SubProtocolConfiguration; import tech.pegasys.pantheon.ethereum.p2p.netty.NettyP2PNetwork; +import tech.pegasys.pantheon.ethereum.p2p.peers.DefaultPeer; +import tech.pegasys.pantheon.ethereum.p2p.peers.Peer; import tech.pegasys.pantheon.ethereum.p2p.peers.PeerBlacklist; +import tech.pegasys.pantheon.ethereum.p2p.peers.cache.PeerCache; import tech.pegasys.pantheon.ethereum.p2p.wire.Capability; import tech.pegasys.pantheon.ethereum.p2p.wire.SubProtocol; import tech.pegasys.pantheon.ethereum.permissioning.AccountWhitelistController; @@ -91,6 +94,7 @@ public class RunnerBuilder { private MetricsConfiguration metricsConfiguration; private MetricsSystem metricsSystem; private Optional permissioningConfiguration = Optional.empty(); + private PeerCache peerCache; private EnodeURL getSelfEnode() { String nodeId = pantheonController.getLocalNodeKeyPair().getPublicKey().toString(); @@ -173,6 +177,11 @@ public RunnerBuilder metricsSystem(final MetricsSystem metricsSystem) { return this; } + public RunnerBuilder peerCache(final PeerCache peerCache) { + this.peerCache = peerCache; + return this; + } + public Runner build() { Preconditions.checkNotNull(pantheonController); @@ -270,6 +279,9 @@ public Runner build() { final PrivacyParameters privacyParameters = pantheonController.getPrivacyParameters(); final FilterManager filterManager = createFilterManager(vertx, context, transactionPool); + final P2PNetwork peerNetwork = networkRunner.getNetwork(); + addCachedPeersToNetwork(peerNetwork); + Optional jsonRpcHttpService = Optional.empty(); if (jsonRpcConfiguration.isEnabled()) { final Map jsonRpcMethods = @@ -277,7 +289,7 @@ public Runner build() { context, protocolSchedule, pantheonController, - networkRunner.getNetwork(), + peerNetwork, synchronizer, transactionPool, miningCoordinator, @@ -286,7 +298,8 @@ public Runner build() { jsonRpcConfiguration.getRpcApis(), filterManager, accountWhitelistController, - privacyParameters); + privacyParameters, + peerCache); jsonRpcHttpService = Optional.of( new JsonRpcHttpService( @@ -300,7 +313,7 @@ public Runner build() { context, protocolSchedule, pantheonController, - networkRunner.getNetwork(), + peerNetwork, synchronizer, transactionPool, miningCoordinator, @@ -309,7 +322,8 @@ public Runner build() { webSocketConfiguration.getRpcApis(), filterManager, accountWhitelistController, - privacyParameters); + privacyParameters, + peerCache); final SubscriptionManager subscriptionManager = createSubscriptionManager(vertx, transactionPool); @@ -368,7 +382,8 @@ private Map jsonRpcMethods( final Collection jsonRpcApis, final FilterManager filterManager, final Optional accountWhitelistController, - final PrivacyParameters privacyParameters) { + final PrivacyParameters privacyParameters, + final PeerCache peerCache) { final Map methods = new JsonRpcMethodsFactory() .methods( @@ -387,7 +402,8 @@ private Map jsonRpcMethods( jsonRpcApis, filterManager, accountWhitelistController, - privacyParameters); + privacyParameters, + peerCache); methods.putAll(pantheonController.getAdditionalJsonRpcMethods(jsonRpcApis)); return methods; } @@ -447,4 +463,13 @@ private MetricsService createMetricsService( final Vertx vertx, final MetricsConfiguration configuration) { return MetricsService.create(vertx, configuration, metricsSystem); } + + private void addCachedPeersToNetwork(final P2PNetwork peerNetwork) { + peerCache.getStaticNodes().stream() + .forEach( + enodeURL -> { + final Peer peer = DefaultPeer.fromEnodeURL(enodeURL); + peerNetwork.addMaintainConnectionPeer(peer); + }); + } } 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..d30e1ea7dd 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java @@ -48,6 +48,8 @@ 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.peers.cache.PeerCache; +import tech.pegasys.pantheon.ethereum.p2p.peers.cache.PersistentJsonPeerCache; import tech.pegasys.pantheon.ethereum.permissioning.LocalPermissioningConfiguration; import tech.pegasys.pantheon.ethereum.permissioning.PermissioningConfigurationBuilder; import tech.pegasys.pantheon.metrics.MetricCategory; @@ -618,6 +620,8 @@ public void run() { permissioningConfiguration.ifPresent( p -> ensureAllBootnodesAreInWhitelist(ethNetworkConfig, p)); + final PeerCache peerCache = createStaticNodesCache(); + synchronize( buildController(), p2pEnabled, @@ -629,7 +633,8 @@ public void run() { jsonRpcConfiguration, webSocketConfiguration, metricsConfiguration(), - permissioningConfiguration); + permissioningConfiguration, + peerCache); } catch (final Exception e) { throw new ParameterException(this.commandLine, e.getMessage(), e); } @@ -847,7 +852,8 @@ private void synchronize( final JsonRpcConfiguration jsonRpcConfiguration, final WebSocketConfiguration webSocketConfiguration, final MetricsConfiguration metricsConfiguration, - final Optional permissioningConfiguration) { + final Optional permissioningConfiguration, + final PeerCache peerCache) { checkNotNull(runnerBuilder); @@ -869,6 +875,7 @@ private void synchronize( .bannedNodeIds(bannedNodeIds) .metricsSystem(metricsSystem.get()) .metricsConfiguration(metricsConfiguration) + .peerCache(peerCache) .build(); addShutdownHook(runner); @@ -1086,4 +1093,11 @@ public MetricsSystem getMetricsSystem() { public PantheonExceptionHandler exceptionHandler() { return exceptionHandlerSupplier.get(); } + + private PeerCache createStaticNodesCache() throws IOException { + final String STATIC_NODES_FILENAME = "static_nodes.json"; + final Path staticNodesPath = dataDir().resolve(STATIC_NODES_FILENAME); + + return PersistentJsonPeerCache.fromPath(staticNodesPath); + } } diff --git a/pantheon/src/test/java/tech/pegasys/pantheon/RunnerTest.java b/pantheon/src/test/java/tech/pegasys/pantheon/RunnerTest.java index 874c992444..faf621ff79 100644 --- a/pantheon/src/test/java/tech/pegasys/pantheon/RunnerTest.java +++ b/pantheon/src/test/java/tech/pegasys/pantheon/RunnerTest.java @@ -12,6 +12,7 @@ */ package tech.pegasys.pantheon; +import static java.util.Collections.emptySet; import static org.assertj.core.api.Assertions.assertThat; import static tech.pegasys.pantheon.cli.EthNetworkConfig.DEV_NETWORK_ID; import static tech.pegasys.pantheon.cli.NetworkName.DEV; @@ -39,6 +40,8 @@ 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.p2p.peers.cache.PeerCache; +import tech.pegasys.pantheon.ethereum.p2p.peers.cache.PersistentJsonPeerCache; import tech.pegasys.pantheon.ethereum.permissioning.LocalPermissioningConfiguration; import tech.pegasys.pantheon.ethereum.storage.StorageProvider; import tech.pegasys.pantheon.ethereum.storage.keyvalue.RocksDbStorageProvider; @@ -52,6 +55,7 @@ import java.net.InetAddress; import java.net.URI; import java.nio.file.Path; +import java.nio.file.Paths; import java.time.Duration; import java.util.Collections; import java.util.List; @@ -138,6 +142,8 @@ private void syncFromGenesis(final SyncMode mode) throws Exception { final MetricsConfiguration aheadMetricsConfiguration = metricsConfiguration(); final LocalPermissioningConfiguration aheadPermissioningConfiguration = permissioningConfiguration(); + final PeerCache peerCache = + new PersistentJsonPeerCache(emptySet(), Paths.get("./arbitraryPath.txt")); final RunnerBuilder runnerBuilder = new RunnerBuilder() .vertx(Vertx.vertx()) @@ -146,7 +152,8 @@ private void syncFromGenesis(final SyncMode mode) throws Exception { .discoveryPort(0) .maxPeers(3) .metricsSystem(noOpMetricsSystem) - .bannedNodeIds(Collections.emptySet()); + .bannedNodeIds(emptySet()) + .peerCache(peerCache); Runner runnerBehind = null; final Runner runnerAhead = 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..69bc411665 100644 --- a/pantheon/src/test/java/tech/pegasys/pantheon/cli/CommandTestAbstract.java +++ b/pantheon/src/test/java/tech/pegasys/pantheon/cli/CommandTestAbstract.java @@ -124,6 +124,7 @@ public void initMocks() throws Exception { when(mockRunnerBuilder.bannedNodeIds(any())).thenReturn(mockRunnerBuilder); when(mockRunnerBuilder.metricsSystem(any())).thenReturn(mockRunnerBuilder); when(mockRunnerBuilder.metricsConfiguration(any())).thenReturn(mockRunnerBuilder); + when(mockRunnerBuilder.peerCache(any())).thenReturn(mockRunnerBuilder); when(mockRunnerBuilder.build()).thenReturn(mockRunner); } From f361ec52fab1290e4cbb69b9e193cd635fe988de Mon Sep 17 00:00:00 2001 From: tmohay Date: Fri, 15 Mar 2019 15:58:32 +1100 Subject: [PATCH 02/11] fix spotless --- .../ethereum/p2p/peers/PersistentJsonPeerCacheTest.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/peers/PersistentJsonPeerCacheTest.java b/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/peers/PersistentJsonPeerCacheTest.java index fbe4eaa80a..e19c5cfc48 100644 --- a/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/peers/PersistentJsonPeerCacheTest.java +++ b/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/peers/PersistentJsonPeerCacheTest.java @@ -21,7 +21,6 @@ import java.io.File; import java.io.IOException; -import java.net.URISyntaxException; import java.net.URL; import java.nio.charset.Charset; import java.nio.file.Files; @@ -139,8 +138,7 @@ public void removePeerReturnsFalseIfSuppliedPeerIsNotInList() throws IOException } @Test - public void removePeerReturnsTrueAndRemovesItemIfPeerIsInList() - throws IOException { + public void removePeerReturnsTrueAndRemovesItemIfPeerIsInList() throws IOException { final URL resource = PersistentJsonPeerCacheTest.class.getResource("valid_static_nodes.json"); final Path original = Paths.get(resource.getPath()); final File tempFile = testFolder.newFile("file.txt"); From 251508e1de8161a295430d43f18e6017b72eb0d4 Mon Sep 17 00:00:00 2001 From: tmohay Date: Mon, 18 Mar 2019 11:05:45 +1100 Subject: [PATCH 03/11] Remove from addPeer --- .../ethereum/jsonrpc/JsonRpcMethodsFactory.java | 12 ++++-------- .../jsonrpc/internal/methods/AdminAddPeer.java | 16 ++++------------ 2 files changed, 8 insertions(+), 20 deletions(-) diff --git a/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcMethodsFactory.java b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcMethodsFactory.java index eee97ba47a..b9e7e8938e 100644 --- a/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcMethodsFactory.java +++ b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcMethodsFactory.java @@ -90,7 +90,6 @@ import tech.pegasys.pantheon.ethereum.jsonrpc.internal.results.BlockResultFactory; import tech.pegasys.pantheon.ethereum.mainnet.ProtocolSchedule; import tech.pegasys.pantheon.ethereum.p2p.api.P2PNetwork; -import tech.pegasys.pantheon.ethereum.p2p.peers.cache.PeerCache; import tech.pegasys.pantheon.ethereum.p2p.wire.Capability; import tech.pegasys.pantheon.ethereum.permissioning.AccountWhitelistController; import tech.pegasys.pantheon.ethereum.privacy.PrivateTransactionHandler; @@ -125,8 +124,7 @@ public Map methods( final Collection rpcApis, final FilterManager filterManager, final Optional accountsWhitelistController, - final PrivacyParameters privacyParameters, - final PeerCache peerCache) { + final PrivacyParameters privacyParameters) { final BlockchainQueries blockchainQueries = new BlockchainQueries(blockchain, worldStateArchive); return methods( @@ -144,8 +142,7 @@ public Map methods( supportedCapabilities, accountsWhitelistController, rpcApis, - privacyParameters, - peerCache); + privacyParameters); } public Map methods( @@ -163,8 +160,7 @@ public Map methods( final Set supportedCapabilities, final Optional accountsWhitelistController, final Collection rpcApis, - final PrivacyParameters privacyParameters, - final PeerCache peerCache) { + final PrivacyParameters privacyParameters) { final Map enabledMethods = new HashMap<>(); if (!rpcApis.isEmpty()) { addMethods(enabledMethods, new RpcModules(rpcApis)); @@ -271,7 +267,7 @@ blockchainQueries, new TransactionTracer(blockReplay), parameter), if (rpcApis.contains(RpcApis.ADMIN)) { addMethods( enabledMethods, - new AdminAddPeer(p2pNetwork, parameter, peerCache), + new AdminAddPeer(p2pNetwork, parameter), new AdminNodeInfo( clientVersion, networkId, genesisConfigOptions, p2pNetwork, blockchainQueries), new AdminPeers(p2pNetwork)); diff --git a/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/AdminAddPeer.java b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/AdminAddPeer.java index df8a73ffd3..ea9e6d635a 100644 --- a/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/AdminAddPeer.java +++ b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/AdminAddPeer.java @@ -24,24 +24,18 @@ import tech.pegasys.pantheon.ethereum.p2p.api.P2PNetwork; import tech.pegasys.pantheon.ethereum.p2p.peers.DefaultPeer; import tech.pegasys.pantheon.ethereum.p2p.peers.Peer; -import tech.pegasys.pantheon.ethereum.p2p.peers.cache.PeerCache; -import tech.pegasys.pantheon.util.enode.EnodeURL; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; public class AdminAddPeer implements JsonRpcMethod { - private static final Logger LOG = LogManager.getLogger(); private final P2PNetwork peerNetwork; private final JsonRpcParameter parameters; - private final PeerCache peerCache; - public AdminAddPeer( - final P2PNetwork peerNetwork, final JsonRpcParameter parameters, final PeerCache peerCache) { + public AdminAddPeer(final P2PNetwork peerNetwork, final JsonRpcParameter parameters) { this.peerNetwork = peerNetwork; this.parameters = parameters; - this.peerCache = peerCache; } @Override @@ -56,11 +50,9 @@ public JsonRpcResponse response(final JsonRpcRequest req) { } try { final String enodeString = parameters.required(req.getParams(), 0, String.class); - final EnodeURL enodeURL = new EnodeURL(enodeString); - final Peer peer = DefaultPeer.fromEnodeURL(enodeURL); - boolean addedToNetwork = peerNetwork.addMaintainConnectionPeer(peer); - boolean addedToCache = peerCache.add(enodeURL); - return new JsonRpcSuccessResponse(req.getId(), addedToNetwork && addedToCache); + final Peer peer = DefaultPeer.fromURI(enodeString); + final boolean added = peerNetwork.addMaintainConnectionPeer(peer); + return new JsonRpcSuccessResponse(req.getId(), added); } catch (final InvalidJsonRpcParameters e) { return new JsonRpcErrorResponse(req.getId(), JsonRpcError.INVALID_PARAMS); } catch (final IllegalArgumentException e) { From c1b6ae245aa370f26199d81d1acb4b819b31e247 Mon Sep 17 00:00:00 2001 From: tmohay Date: Mon, 18 Mar 2019 11:11:20 +1100 Subject: [PATCH 04/11] Removed excess code --- .../jsonrpc/JsonRpcTestMethodsFactory.java | 5 +- .../AbstractEthJsonRpcHttpServiceTest.java | 4 +- .../JsonRpcHttpServiceHostWhitelistTest.java | 4 +- .../jsonrpc/JsonRpcHttpServiceLoginTest.java | 4 +- .../JsonRpcHttpServiceRpcApisTest.java | 4 +- .../jsonrpc/JsonRpcHttpServiceTest.java | 4 +- .../internal/methods/AdminAddPeerTest.java | 115 +++++++++++------- .../ethereum/p2p/peers/DefaultPeer.java | 13 -- .../tech/pegasys/pantheon/RunnerBuilder.java | 12 +- 9 files changed, 82 insertions(+), 83 deletions(-) diff --git a/ethereum/jsonrpc/src/integration-test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcTestMethodsFactory.java b/ethereum/jsonrpc/src/integration-test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcTestMethodsFactory.java index f38ef5b7b2..cf331a51f7 100644 --- a/ethereum/jsonrpc/src/integration-test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcTestMethodsFactory.java +++ b/ethereum/jsonrpc/src/integration-test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcTestMethodsFactory.java @@ -35,7 +35,6 @@ import tech.pegasys.pantheon.ethereum.mainnet.ProtocolSchedule; import tech.pegasys.pantheon.ethereum.mainnet.ProtocolSpec; import tech.pegasys.pantheon.ethereum.p2p.api.P2PNetwork; -import tech.pegasys.pantheon.ethereum.p2p.peers.cache.PeerCache; import tech.pegasys.pantheon.ethereum.permissioning.AccountWhitelistController; import tech.pegasys.pantheon.ethereum.worldstate.WorldStateArchive; import tech.pegasys.pantheon.metrics.MetricsSystem; @@ -86,7 +85,6 @@ public Map methods() { final Optional accountWhitelistController = Optional.of(mock(AccountWhitelistController.class)); final PrivacyParameters privacyParameters = mock(PrivacyParameters.class); - final PeerCache peerCache = mock(PeerCache.class); return new JsonRpcMethodsFactory() .methods( @@ -104,7 +102,6 @@ public Map methods() { new HashSet<>(), accountWhitelistController, RpcApis.DEFAULT_JSON_RPC_APIS, - privacyParameters, - peerCache); + privacyParameters); } } diff --git a/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/AbstractEthJsonRpcHttpServiceTest.java b/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/AbstractEthJsonRpcHttpServiceTest.java index 6b12e5bb7b..37264f01fc 100644 --- a/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/AbstractEthJsonRpcHttpServiceTest.java +++ b/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/AbstractEthJsonRpcHttpServiceTest.java @@ -45,7 +45,6 @@ import tech.pegasys.pantheon.ethereum.mainnet.ProtocolSpec; import tech.pegasys.pantheon.ethereum.mainnet.ValidationResult; import tech.pegasys.pantheon.ethereum.p2p.api.P2PNetwork; -import tech.pegasys.pantheon.ethereum.p2p.peers.cache.PeerCache; import tech.pegasys.pantheon.ethereum.p2p.wire.Capability; import tech.pegasys.pantheon.ethereum.util.RawBlockIterator; import tech.pegasys.pantheon.ethereum.worldstate.WorldStateArchive; @@ -188,8 +187,7 @@ public void setupTest() throws Exception { supportedCapabilities, Optional.empty(), JSON_RPC_APIS, - privacyParameters, - mock(PeerCache.class)); + privacyParameters); final JsonRpcConfiguration config = JsonRpcConfiguration.createDefault(); config.setPort(0); service = diff --git a/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcHttpServiceHostWhitelistTest.java b/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcHttpServiceHostWhitelistTest.java index 5f6cffd53b..26c2e3752a 100644 --- a/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcHttpServiceHostWhitelistTest.java +++ b/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcHttpServiceHostWhitelistTest.java @@ -27,7 +27,6 @@ import tech.pegasys.pantheon.ethereum.jsonrpc.internal.queries.BlockchainQueries; import tech.pegasys.pantheon.ethereum.mainnet.MainnetProtocolSchedule; import tech.pegasys.pantheon.ethereum.p2p.api.P2PNetwork; -import tech.pegasys.pantheon.ethereum.p2p.peers.cache.PeerCache; import tech.pegasys.pantheon.ethereum.p2p.wire.Capability; import tech.pegasys.pantheon.ethereum.permissioning.AccountWhitelistController; import tech.pegasys.pantheon.metrics.noop.NoOpMetricsSystem; @@ -102,8 +101,7 @@ public void initServerAndClient() throws Exception { supportedCapabilities, Optional.of(mock(AccountWhitelistController.class)), JSON_RPC_APIS, - mock(PrivacyParameters.class), - mock(PeerCache.class))); + mock(PrivacyParameters.class))); service = createJsonRpcHttpService(); service.start().join(); diff --git a/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcHttpServiceLoginTest.java b/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcHttpServiceLoginTest.java index f6264c4816..abd3d94b8e 100644 --- a/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcHttpServiceLoginTest.java +++ b/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcHttpServiceLoginTest.java @@ -33,7 +33,6 @@ import tech.pegasys.pantheon.ethereum.jsonrpc.internal.queries.BlockchainQueries; import tech.pegasys.pantheon.ethereum.mainnet.MainnetProtocolSchedule; import tech.pegasys.pantheon.ethereum.p2p.api.P2PNetwork; -import tech.pegasys.pantheon.ethereum.p2p.peers.cache.PeerCache; import tech.pegasys.pantheon.ethereum.p2p.wire.Capability; import tech.pegasys.pantheon.metrics.noop.NoOpMetricsSystem; @@ -133,8 +132,7 @@ public static void initServerAndClient() throws Exception { supportedCapabilities, Optional.empty(), JSON_RPC_APIS, - mock(PrivacyParameters.class), - mock(PeerCache.class))); + mock(PrivacyParameters.class))); service = createJsonRpcHttpService(); jwtAuth = service.authenticationService.get().getJwtAuthProvider(); service.start().join(); diff --git a/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcHttpServiceRpcApisTest.java b/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcHttpServiceRpcApisTest.java index e4814ce44e..eb10d8e40e 100644 --- a/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcHttpServiceRpcApisTest.java +++ b/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcHttpServiceRpcApisTest.java @@ -29,7 +29,6 @@ import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcError; import tech.pegasys.pantheon.ethereum.mainnet.MainnetProtocolSchedule; import tech.pegasys.pantheon.ethereum.p2p.api.P2PNetwork; -import tech.pegasys.pantheon.ethereum.p2p.peers.cache.PeerCache; import tech.pegasys.pantheon.ethereum.p2p.wire.Capability; import tech.pegasys.pantheon.ethereum.permissioning.AccountWhitelistController; import tech.pegasys.pantheon.metrics.noop.NoOpMetricsSystem; @@ -185,8 +184,7 @@ private JsonRpcHttpService createJsonRpcHttpServiceWithRpcApis(final JsonRpcConf supportedCapabilities, Optional.of(mock(AccountWhitelistController.class)), config.getRpcApis(), - mock(PrivacyParameters.class), - mock(PeerCache.class))); + mock(PrivacyParameters.class))); final JsonRpcHttpService jsonRpcHttpService = new JsonRpcHttpService( vertx, folder.newFolder().toPath(), config, new NoOpMetricsSystem(), rpcMethods); diff --git a/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcHttpServiceTest.java b/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcHttpServiceTest.java index fb8377e5e2..b4d7431c5e 100644 --- a/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcHttpServiceTest.java +++ b/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcHttpServiceTest.java @@ -42,7 +42,6 @@ import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcError; import tech.pegasys.pantheon.ethereum.mainnet.MainnetProtocolSchedule; import tech.pegasys.pantheon.ethereum.p2p.api.P2PNetwork; -import tech.pegasys.pantheon.ethereum.p2p.peers.cache.PeerCache; import tech.pegasys.pantheon.ethereum.p2p.wire.Capability; import tech.pegasys.pantheon.ethereum.permissioning.AccountWhitelistController; import tech.pegasys.pantheon.metrics.noop.NoOpMetricsSystem; @@ -130,8 +129,7 @@ public static void initServerAndClient() throws Exception { supportedCapabilities, Optional.of(mock(AccountWhitelistController.class)), JSON_RPC_APIS, - mock(PrivacyParameters.class), - mock(PeerCache.class))); + mock(PrivacyParameters.class))); service = createJsonRpcHttpService(); service.start().join(); diff --git a/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/AdminAddPeerTest.java b/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/AdminAddPeerTest.java index e9bde201b4..bbe316380d 100644 --- a/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/AdminAddPeerTest.java +++ b/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/AdminAddPeerTest.java @@ -14,11 +14,8 @@ 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.verify; import static org.mockito.Mockito.when; -import tech.pegasys.pantheon.crypto.SECP256K1.PublicKey; import tech.pegasys.pantheon.ethereum.jsonrpc.internal.JsonRpcRequest; import tech.pegasys.pantheon.ethereum.jsonrpc.internal.parameters.JsonRpcParameter; import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcError; @@ -28,10 +25,6 @@ import tech.pegasys.pantheon.ethereum.p2p.P2pDisabledException; import tech.pegasys.pantheon.ethereum.p2p.PeerNotWhitelistedException; import tech.pegasys.pantheon.ethereum.p2p.api.P2PNetwork; -import tech.pegasys.pantheon.ethereum.p2p.peers.cache.PeerCache; -import tech.pegasys.pantheon.util.enode.EnodeURL; - -import java.math.BigInteger; import org.junit.Before; import org.junit.Test; @@ -43,23 +36,13 @@ public class AdminAddPeerTest { @Mock private P2PNetwork p2pNetwork; - @Mock private PeerCache peerCache; private final JsonRpcParameter parameter = new JsonRpcParameter(); private AdminAddPeer method; - private final EnodeURL validEnodeURL = - new EnodeURL( - PublicKey.create(BigInteger.valueOf(0)).toString().substring(2), "127.0.0.1", 30303); - - private final JsonRpcRequest validRequest = - new JsonRpcRequest("2.0", "admin_addPeer", new String[] {validEnodeURL.toString()}); - @Before public void setup() { - - method = new AdminAddPeer(p2pNetwork, parameter, peerCache); - when(peerCache.add(any())).thenReturn(true); + method = new AdminAddPeer(p2pNetwork, parameter); } @Test @@ -111,38 +94,46 @@ public void requestHasInvalidEnode() { public void requestAddsValidEnode() { when(p2pNetwork.addMaintainConnectionPeer(any())).thenReturn(true); - final JsonRpcResponse expectedResponse = new JsonRpcSuccessResponse(validRequest.getId(), true); - - final JsonRpcResponse actualResponse = method.response(validRequest); - - assertThat(actualResponse).isEqualToComparingFieldByField(expectedResponse); - verify(peerCache).add(eq(validEnodeURL)); - } - - @Test - public void ifPeerIsNotAddedToCacheFalseIsReturned() { - when(p2pNetwork.addMaintainConnectionPeer(any())).thenReturn(true); - when(peerCache.add(any())).thenReturn(false); - final JsonRpcRequest request = - new JsonRpcRequest("2.0", "admin_addPeer", new String[] {validEnodeURL.toString()}); + new JsonRpcRequest( + "2.0", + "admin_addPeer", + new String[] { + "enode://" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "@127.0.0.1:30303" + }); - final JsonRpcResponse expectedResponse = new JsonRpcSuccessResponse(request.getId(), false); + final JsonRpcResponse expectedResponse = new JsonRpcSuccessResponse(request.getId(), true); final JsonRpcResponse actualResponse = method.response(request); assertThat(actualResponse).isEqualToComparingFieldByField(expectedResponse); - verify(peerCache).add(eq(validEnodeURL)); } @Test public void requestRefusesListOfNodes() { - final JsonRpcRequest request = new JsonRpcRequest( "2.0", "admin_addPeer", - new String[] {validEnodeURL.toString(), validEnodeURL.toString()}); + new String[] { + "enode://" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "@127.0.0.1:30303", + "enode://" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000001" + + "@127.0.0.2:30303" + }); final JsonRpcResponse expectedResponse = new JsonRpcErrorResponse(request.getId(), JsonRpcError.INVALID_PARAMS); @@ -156,10 +147,22 @@ public void requestRefusesListOfNodes() { public void requestReturnsFalseIfAddFails() { when(p2pNetwork.addMaintainConnectionPeer(any())).thenReturn(false); - final JsonRpcResponse expectedResponse = - new JsonRpcSuccessResponse(validRequest.getId(), false); + final JsonRpcRequest request = + new JsonRpcRequest( + "2.0", + "admin_addPeer", + new String[] { + "enode://" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "@127.0.0.1:30303" + }); - final JsonRpcResponse actualResponse = method.response(validRequest); + final JsonRpcResponse expectedResponse = new JsonRpcSuccessResponse(request.getId(), false); + + final JsonRpcResponse actualResponse = method.response(request); assertThat(actualResponse).isEqualToComparingFieldByField(expectedResponse); } @@ -170,10 +173,23 @@ public void requestReturnsErrorWhenP2pDisabled() { .thenThrow( new P2pDisabledException("P2P networking disabled. Unable to connect to add peer.")); + final JsonRpcRequest request = + new JsonRpcRequest( + "2.0", + "admin_addPeer", + new String[] { + "enode://" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "@127.0.0.1:30303" + }); + final JsonRpcResponse expectedResponse = - new JsonRpcErrorResponse(validRequest.getId(), JsonRpcError.P2P_DISABLED); + new JsonRpcErrorResponse(request.getId(), JsonRpcError.P2P_DISABLED); - final JsonRpcResponse actualResponse = method.response(validRequest); + final JsonRpcResponse actualResponse = method.response(request); assertThat(actualResponse).isEqualToComparingFieldByField(expectedResponse); } @@ -183,11 +199,24 @@ public void requestReturnsErrorWhenPeerNotWhitelisted() { when(p2pNetwork.addMaintainConnectionPeer(any())) .thenThrow(new PeerNotWhitelistedException("Cannot add peer that is not whitelisted")); + final JsonRpcRequest request = + new JsonRpcRequest( + "2.0", + "admin_addPeer", + new String[] { + "enode://" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "@127.0.0.1:30303" + }); + final JsonRpcResponse expectedResponse = new JsonRpcErrorResponse( - validRequest.getId(), JsonRpcError.NON_WHITELISTED_NODE_CANNOT_BE_ADDED_AS_A_PEER); + request.getId(), JsonRpcError.NON_WHITELISTED_NODE_CANNOT_BE_ADDED_AS_A_PEER); - final JsonRpcResponse actualResponse = method.response(validRequest); + final JsonRpcResponse actualResponse = method.response(request); assertThat(actualResponse).isEqualToComparingFieldByField(expectedResponse); } diff --git a/ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/peers/DefaultPeer.java b/ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/peers/DefaultPeer.java index 0ba705b673..b1c94d19aa 100644 --- a/ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/peers/DefaultPeer.java +++ b/ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/peers/DefaultPeer.java @@ -20,7 +20,6 @@ import tech.pegasys.pantheon.ethereum.rlp.RLPInput; import tech.pegasys.pantheon.util.NetworkUtility; import tech.pegasys.pantheon.util.bytes.BytesValue; -import tech.pegasys.pantheon.util.enode.EnodeURL; import java.net.URI; import java.util.Objects; @@ -43,18 +42,6 @@ public class DefaultPeer extends DefaultPeerId implements Peer { private final Endpoint endpoint; - public static DefaultPeer fromEnodeURL(final EnodeURL enodeURL) { - final int udpPort = enodeURL.getDiscoveryPort().orElse(enodeURL.getListeningPort()); - - final Endpoint endpoint = - new Endpoint( - enodeURL.getInetAddress().getHostAddress(), - udpPort, - OptionalInt.of(enodeURL.getListeningPort())); - - return new DefaultPeer(BytesValue.fromHexString(enodeURL.getNodeId()), endpoint); - } - /** * Creates a {@link DefaultPeer} instance from a String representation of an enode URL. * diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/RunnerBuilder.java b/pantheon/src/main/java/tech/pegasys/pantheon/RunnerBuilder.java index 1860ae8055..6d5cfd279c 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/RunnerBuilder.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/RunnerBuilder.java @@ -298,8 +298,7 @@ public Runner build() { jsonRpcConfiguration.getRpcApis(), filterManager, accountWhitelistController, - privacyParameters, - peerCache); + privacyParameters); jsonRpcHttpService = Optional.of( new JsonRpcHttpService( @@ -322,8 +321,7 @@ public Runner build() { webSocketConfiguration.getRpcApis(), filterManager, accountWhitelistController, - privacyParameters, - peerCache); + privacyParameters); final SubscriptionManager subscriptionManager = createSubscriptionManager(vertx, transactionPool); @@ -382,8 +380,7 @@ private Map jsonRpcMethods( final Collection jsonRpcApis, final FilterManager filterManager, final Optional accountWhitelistController, - final PrivacyParameters privacyParameters, - final PeerCache peerCache) { + final PrivacyParameters privacyParameters) { final Map methods = new JsonRpcMethodsFactory() .methods( @@ -402,8 +399,7 @@ private Map jsonRpcMethods( jsonRpcApis, filterManager, accountWhitelistController, - privacyParameters, - peerCache); + privacyParameters); methods.putAll(pantheonController.getAdditionalJsonRpcMethods(jsonRpcApis)); return methods; } From b0ae78251d1cf086afcedeb1f9a34ca3018e5189 Mon Sep 17 00:00:00 2001 From: tmohay Date: Mon, 18 Mar 2019 11:23:56 +1100 Subject: [PATCH 05/11] StaticNodes loaded at startup --- .../ethereum/p2p/peers/DefaultPeer.java | 13 +++ ...nPeerCache.java => StaticNodesParser.java} | 44 +++------- .../tech/pegasys/pantheon/RunnerBuilder.java | 34 ++++---- .../pegasys/pantheon/cli/PantheonCommand.java | 82 ++++++++----------- 4 files changed, 76 insertions(+), 97 deletions(-) rename ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/peers/cache/{PersistentJsonPeerCache.java => StaticNodesParser.java} (69%) diff --git a/ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/peers/DefaultPeer.java b/ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/peers/DefaultPeer.java index b1c94d19aa..0ba705b673 100644 --- a/ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/peers/DefaultPeer.java +++ b/ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/peers/DefaultPeer.java @@ -20,6 +20,7 @@ import tech.pegasys.pantheon.ethereum.rlp.RLPInput; import tech.pegasys.pantheon.util.NetworkUtility; import tech.pegasys.pantheon.util.bytes.BytesValue; +import tech.pegasys.pantheon.util.enode.EnodeURL; import java.net.URI; import java.util.Objects; @@ -42,6 +43,18 @@ public class DefaultPeer extends DefaultPeerId implements Peer { private final Endpoint endpoint; + public static DefaultPeer fromEnodeURL(final EnodeURL enodeURL) { + final int udpPort = enodeURL.getDiscoveryPort().orElse(enodeURL.getListeningPort()); + + final Endpoint endpoint = + new Endpoint( + enodeURL.getInetAddress().getHostAddress(), + udpPort, + OptionalInt.of(enodeURL.getListeningPort())); + + return new DefaultPeer(BytesValue.fromHexString(enodeURL.getNodeId()), endpoint); + } + /** * Creates a {@link DefaultPeer} instance from a String representation of an enode URL. * diff --git a/ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/peers/cache/PersistentJsonPeerCache.java b/ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/peers/cache/StaticNodesParser.java similarity index 69% rename from ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/peers/cache/PersistentJsonPeerCache.java rename to ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/peers/cache/StaticNodesParser.java index 33ada81cec..50e2c49cce 100644 --- a/ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/peers/cache/PersistentJsonPeerCache.java +++ b/ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/peers/cache/StaticNodesParser.java @@ -12,36 +12,26 @@ */ package tech.pegasys.pantheon.ethereum.p2p.peers.cache; -import tech.pegasys.pantheon.util.enode.EnodeURL; +import static java.nio.charset.StandardCharsets.UTF_8; +import io.vertx.core.json.DecodeException; +import io.vertx.core.json.JsonArray; import java.io.FileNotFoundException; import java.io.IOException; -import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.NoSuchFileException; import java.nio.file.Path; import java.util.HashSet; import java.util.Set; -import java.util.stream.Collectors; - -import io.vertx.core.json.DecodeException; -import io.vertx.core.json.JsonArray; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import tech.pegasys.pantheon.util.enode.EnodeURL; -public class PersistentJsonPeerCache extends PersistentPeerCache { +public class StaticNodesParser { private static final Logger LOG = LogManager.getLogger(); - private static final Charset UTF_8 = Charset.forName("UTF-8"); - private final Path path; - - public PersistentJsonPeerCache(final Set initialNodes, final Path path) { - super(initialNodes); - this.path = path; - } - - public static PersistentJsonPeerCache fromPath(final Path path) + public static Set fromPath(final Path path) throws IOException, IllegalArgumentException { final Set result = new HashSet<>(); @@ -49,11 +39,11 @@ public static PersistentJsonPeerCache fromPath(final Path path) try { staticNodesContent = Files.readAllBytes(path); if (staticNodesContent.length == 0) { - return new PersistentJsonPeerCache(result, path); + return result; } } catch (FileNotFoundException | NoSuchFileException ex) { LOG.info("No StaticNodes file ({}) exists, creating empty cache.", path); - return new PersistentJsonPeerCache(result, path); + return result; } catch (IOException ex) { LOG.info("Unable to parse static nodes file ({})", path); throw ex; @@ -65,7 +55,6 @@ public static PersistentJsonPeerCache fromPath(final Path path) final String enodeString = (String) jsonObj; result.add(decodeString(enodeString)); } - return new PersistentJsonPeerCache(result, path); } catch (DecodeException ex) { LOG.info("Content of ({}} was invalid json, and could not be decoded.", path); throw ex; @@ -73,6 +62,8 @@ public static PersistentJsonPeerCache fromPath(final Path path) LOG.info("Parsing ({}) has failed due incorrectly formatted enode element.", path); throw ex; } + + return result; } private static EnodeURL decodeString(final String input) { @@ -83,19 +74,4 @@ private static EnodeURL decodeString(final String input) { throw ex; } } - - // TODO: This should potentially be using an atomicMove to prevent file corruption - // on sudden terminations. - @Override - protected void persist() { - final JsonArray array = - new JsonArray( - getStaticNodes().stream().map(EnodeURL::toString).collect(Collectors.toList())); - - try { - Files.write(path, array.toString().getBytes(UTF_8)); - } catch (IOException ex) { - LOG.info("Unable to persist peers to disk, file write failed."); - } - } } diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/RunnerBuilder.java b/pantheon/src/main/java/tech/pegasys/pantheon/RunnerBuilder.java index 6d5cfd279c..6aca227aa0 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/RunnerBuilder.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/RunnerBuilder.java @@ -12,6 +12,7 @@ */ package tech.pegasys.pantheon; +import java.io.IOException; import tech.pegasys.pantheon.cli.EthNetworkConfig; import tech.pegasys.pantheon.controller.PantheonController; import tech.pegasys.pantheon.crypto.SECP256K1.KeyPair; @@ -53,6 +54,8 @@ import tech.pegasys.pantheon.ethereum.p2p.peers.Peer; import tech.pegasys.pantheon.ethereum.p2p.peers.PeerBlacklist; import tech.pegasys.pantheon.ethereum.p2p.peers.cache.PeerCache; +import tech.pegasys.pantheon.ethereum.p2p.peers.cache.PersistentJsonPeerCache; +import tech.pegasys.pantheon.ethereum.p2p.peers.cache.StaticNodesParser; import tech.pegasys.pantheon.ethereum.p2p.wire.Capability; import tech.pegasys.pantheon.ethereum.p2p.wire.SubProtocol; import tech.pegasys.pantheon.ethereum.permissioning.AccountWhitelistController; @@ -94,7 +97,6 @@ public class RunnerBuilder { private MetricsConfiguration metricsConfiguration; private MetricsSystem metricsSystem; private Optional permissioningConfiguration = Optional.empty(); - private PeerCache peerCache; private EnodeURL getSelfEnode() { String nodeId = pantheonController.getLocalNodeKeyPair().getPublicKey().toString(); @@ -177,12 +179,7 @@ public RunnerBuilder metricsSystem(final MetricsSystem metricsSystem) { return this; } - public RunnerBuilder peerCache(final PeerCache peerCache) { - this.peerCache = peerCache; - return this; - } - - public Runner build() { + public Runner build() throws IOException { Preconditions.checkNotNull(pantheonController); @@ -280,7 +277,7 @@ public Runner build() { final FilterManager filterManager = createFilterManager(vertx, context, transactionPool); final P2PNetwork peerNetwork = networkRunner.getNetwork(); - addCachedPeersToNetwork(peerNetwork); + addStaticPeersToNetwork(peerNetwork); Optional jsonRpcHttpService = Optional.empty(); if (jsonRpcConfiguration.isEnabled()) { @@ -460,12 +457,19 @@ private MetricsService createMetricsService( return MetricsService.create(vertx, configuration, metricsSystem); } - private void addCachedPeersToNetwork(final P2PNetwork peerNetwork) { - peerCache.getStaticNodes().stream() - .forEach( - enodeURL -> { - final Peer peer = DefaultPeer.fromEnodeURL(enodeURL); - peerNetwork.addMaintainConnectionPeer(peer); - }); + + private PeerCache addStaticPeersToNetwork(final P2PNetwork peerNetwork) throws IOException { + final String STATIC_NODES_FILENAME = "static_nodes.json"; + final Path staticNodesPath = dataDir.resolve(STATIC_NODES_FILENAME); + + final Set staticPeers = StaticNodesParser.fromPath(staticNodesPath); + + staticPeers.stream().forEach(enodeURL -> { + final Peer peer = DefaultPeer.fromEnodeURL(enodeURL); + peerNetwork.addMaintainConnectionPeer(peer); + });) + + + } } 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 d30e1ea7dd..53eab664d2 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java @@ -25,6 +25,38 @@ import static tech.pegasys.pantheon.metrics.prometheus.MetricsConfiguration.DEFAULT_METRICS_PUSH_PORT; import static tech.pegasys.pantheon.metrics.prometheus.MetricsConfiguration.createDefault; +import com.google.common.base.Suppliers; +import com.google.common.io.Resources; +import io.vertx.core.Vertx; +import io.vertx.core.json.DecodeException; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.InetAddress; +import java.net.URI; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.core.config.Configurator; +import picocli.CommandLine; +import picocli.CommandLine.AbstractParseResultHandler; +import picocli.CommandLine.Command; +import picocli.CommandLine.ExecutionException; +import picocli.CommandLine.ITypeConverter; +import picocli.CommandLine.Option; +import picocli.CommandLine.ParameterException; import tech.pegasys.pantheon.Runner; import tech.pegasys.pantheon.RunnerBuilder; import tech.pegasys.pantheon.cli.PublicKeySubCommand.KeyLoader; @@ -49,7 +81,6 @@ import tech.pegasys.pantheon.ethereum.jsonrpc.RpcApis; import tech.pegasys.pantheon.ethereum.jsonrpc.websocket.WebSocketConfiguration; import tech.pegasys.pantheon.ethereum.p2p.peers.cache.PeerCache; -import tech.pegasys.pantheon.ethereum.p2p.peers.cache.PersistentJsonPeerCache; import tech.pegasys.pantheon.ethereum.permissioning.LocalPermissioningConfiguration; import tech.pegasys.pantheon.ethereum.permissioning.PermissioningConfigurationBuilder; import tech.pegasys.pantheon.metrics.MetricCategory; @@ -62,40 +93,6 @@ import tech.pegasys.pantheon.util.bytes.BytesValue; import tech.pegasys.pantheon.util.enode.EnodeURL; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.net.InetAddress; -import java.net.URI; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import java.util.Optional; -import java.util.Set; -import java.util.function.Function; -import java.util.function.Supplier; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import com.google.common.base.Suppliers; -import com.google.common.io.Resources; -import io.vertx.core.Vertx; -import io.vertx.core.json.DecodeException; -import org.apache.logging.log4j.Level; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.apache.logging.log4j.core.config.Configurator; -import picocli.CommandLine; -import picocli.CommandLine.AbstractParseResultHandler; -import picocli.CommandLine.Command; -import picocli.CommandLine.ExecutionException; -import picocli.CommandLine.ITypeConverter; -import picocli.CommandLine.Option; -import picocli.CommandLine.ParameterException; - @SuppressWarnings("FieldCanBeLocal") // because Picocli injected fields report false positives @Command( description = "This command runs the Pantheon Ethereum client full node.", @@ -620,8 +617,6 @@ public void run() { permissioningConfiguration.ifPresent( p -> ensureAllBootnodesAreInWhitelist(ethNetworkConfig, p)); - final PeerCache peerCache = createStaticNodesCache(); - synchronize( buildController(), p2pEnabled, @@ -633,8 +628,7 @@ public void run() { jsonRpcConfiguration, webSocketConfiguration, metricsConfiguration(), - permissioningConfiguration, - peerCache); + permissioningConfiguration); } catch (final Exception e) { throw new ParameterException(this.commandLine, e.getMessage(), e); } @@ -852,8 +846,7 @@ private void synchronize( final JsonRpcConfiguration jsonRpcConfiguration, final WebSocketConfiguration webSocketConfiguration, final MetricsConfiguration metricsConfiguration, - final Optional permissioningConfiguration, - final PeerCache peerCache) { + final Optional permissioningConfiguration) { checkNotNull(runnerBuilder); @@ -875,7 +868,6 @@ private void synchronize( .bannedNodeIds(bannedNodeIds) .metricsSystem(metricsSystem.get()) .metricsConfiguration(metricsConfiguration) - .peerCache(peerCache) .build(); addShutdownHook(runner); @@ -1094,10 +1086,4 @@ public PantheonExceptionHandler exceptionHandler() { return exceptionHandlerSupplier.get(); } - private PeerCache createStaticNodesCache() throws IOException { - final String STATIC_NODES_FILENAME = "static_nodes.json"; - final Path staticNodesPath = dataDir().resolve(STATIC_NODES_FILENAME); - - return PersistentJsonPeerCache.fromPath(staticNodesPath); - } } From cd08c92fbb0559673a1479b8119030379363dbba Mon Sep 17 00:00:00 2001 From: tmohay Date: Tue, 19 Mar 2019 13:00:28 +1100 Subject: [PATCH 06/11] No More mods during RPC --- .../peers/{cache => }/StaticNodesParser.java | 2 +- .../ethereum/p2p/peers/cache/PeerCache.java | 41 ----- .../p2p/peers/cache/PersistentPeerCache.java | 44 ----- .../peers/PersistentJsonPeerCacheTest.java | 157 ------------------ .../p2p/peers/StaticeNodesParserTest.java | 91 ++++++++++ .../tech/pegasys/pantheon/RunnerBuilder.java | 55 +++--- .../pegasys/pantheon/cli/PantheonCommand.java | 82 +++++---- .../tech/pegasys/pantheon/RunnerTest.java | 59 +++---- .../pantheon/cli/CommandTestAbstract.java | 2 +- 9 files changed, 188 insertions(+), 345 deletions(-) rename ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/peers/{cache => }/StaticNodesParser.java (97%) delete mode 100644 ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/peers/cache/PeerCache.java delete mode 100644 ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/peers/cache/PersistentPeerCache.java delete mode 100644 ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/peers/PersistentJsonPeerCacheTest.java create mode 100644 ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/peers/StaticeNodesParserTest.java diff --git a/ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/peers/cache/StaticNodesParser.java b/ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/peers/StaticNodesParser.java similarity index 97% rename from ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/peers/cache/StaticNodesParser.java rename to ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/peers/StaticNodesParser.java index 50e2c49cce..77cf3427fc 100644 --- a/ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/peers/cache/StaticNodesParser.java +++ b/ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/peers/StaticNodesParser.java @@ -10,7 +10,7 @@ * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. */ -package tech.pegasys.pantheon.ethereum.p2p.peers.cache; +package tech.pegasys.pantheon.ethereum.p2p.peers; import static java.nio.charset.StandardCharsets.UTF_8; diff --git a/ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/peers/cache/PeerCache.java b/ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/peers/cache/PeerCache.java deleted file mode 100644 index 070648516a..0000000000 --- a/ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/peers/cache/PeerCache.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2019 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.peers.cache; - -import tech.pegasys.pantheon.util.enode.EnodeURL; - -import java.util.HashSet; -import java.util.Set; - -import com.google.common.collect.ImmutableList; - -public class PeerCache { - - private final Set nodes = new HashSet<>(); - - public PeerCache(final Set initialNodes) { - nodes.addAll(initialNodes); - } - - public boolean add(final EnodeURL enode) throws IllegalArgumentException { - return nodes.add(enode); - } - - public boolean remove(final EnodeURL enode) { - return nodes.remove(enode); - } - - public ImmutableList getStaticNodes() { - return ImmutableList.copyOf(nodes); - } -} diff --git a/ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/peers/cache/PersistentPeerCache.java b/ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/peers/cache/PersistentPeerCache.java deleted file mode 100644 index befae4c800..0000000000 --- a/ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/peers/cache/PersistentPeerCache.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2019 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.peers.cache; - -import tech.pegasys.pantheon.util.enode.EnodeURL; - -import java.util.Set; - -public abstract class PersistentPeerCache extends PeerCache { - - public PersistentPeerCache(final Set initialNodes) { - super(initialNodes); - } - - @Override - public boolean add(final EnodeURL enode) throws IllegalArgumentException { - boolean cachedNodesAltered = super.add(enode); - if (cachedNodesAltered) { - persist(); - } - return cachedNodesAltered; - } - - @Override - public boolean remove(final EnodeURL enode) { - boolean cachedNodesAltered = super.remove(enode); - if (cachedNodesAltered) { - persist(); - } - return cachedNodesAltered; - } - - protected abstract void persist(); -} diff --git a/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/peers/PersistentJsonPeerCacheTest.java b/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/peers/PersistentJsonPeerCacheTest.java deleted file mode 100644 index e19c5cfc48..0000000000 --- a/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/peers/PersistentJsonPeerCacheTest.java +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright 2019 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.peers; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - -import tech.pegasys.pantheon.ethereum.p2p.peers.cache.PeerCache; -import tech.pegasys.pantheon.ethereum.p2p.peers.cache.PersistentJsonPeerCache; -import tech.pegasys.pantheon.util.enode.EnodeURL; - -import java.io.File; -import java.io.IOException; -import java.net.URL; -import java.nio.charset.Charset; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.nio.file.StandardCopyOption; - -import com.google.common.collect.ImmutableList; -import io.vertx.core.json.DecodeException; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; - -public class PersistentJsonPeerCacheTest { - - // NOTE: The invalid_static_nodes file is identical to the valid, however one node's port is set - // to "A". - - // First peer ion the valid_static_nodes file. - private final EnodeURL firstItemInValidList = - new EnodeURL( - "50203c6bfca6874370e71aecc8958529fd723feb05013dc1abca8fc1fff845c5259faba05852e9dfe5ce172a7d6e7c2a3a5eaa8b541c8af15ea5518bbff5f2fa", - "127.0.0.1", - 30303); - - @Rule public TemporaryFolder testFolder = new TemporaryFolder(); - - @Test - public void validFileLoadsWithExpectedNumberOfPeers() throws IOException { - final URL resource = PersistentJsonPeerCacheTest.class.getResource("valid_static_nodes.json"); - final Path path = Paths.get(resource.getPath()); - - final PeerCache cache = PersistentJsonPeerCache.fromPath(path); - final ImmutableList enodes = cache.getStaticNodes(); - - assertThat(enodes.size()).isEqualTo(4); - } - - @Test - public void invalidFileThrowsAnException() { - final URL resource = PersistentJsonPeerCacheTest.class.getResource("invalid_static_nodes.json"); - final Path path = Paths.get(resource.getPath()); - - assertThatThrownBy(() -> PersistentJsonPeerCache.fromPath(path)) - .isInstanceOf(IllegalArgumentException.class); - } - - @Test - public void nonJsonFileThrowsAnException() throws IOException { - final File tempFile = testFolder.newFile("file.txt"); - tempFile.deleteOnExit(); - Files.write(tempFile.toPath(), "This Is Not Json".getBytes(Charset.forName("UTF-8"))); - - assertThatThrownBy(() -> PersistentJsonPeerCache.fromPath(tempFile.toPath())) - .isInstanceOf(DecodeException.class); - } - - @Test - public void anEmptyCacheIsCreatedIfTheFileDoesNotExist() throws IOException { - final Path path = Paths.get("./arbirtraryFilename.txt"); - - final PeerCache cache = PersistentJsonPeerCache.fromPath(path); - assertThat(cache.getStaticNodes().size()).isZero(); - } - - @Test - public void cacheIsCreatedIfFileExistsButIsEmpty() throws IOException { - final File tempFile = testFolder.newFile("file.txt"); - tempFile.deleteOnExit(); - - final PeerCache cache = PersistentJsonPeerCache.fromPath(tempFile.toPath()); - final ImmutableList enodes = cache.getStaticNodes(); - - assertThat(enodes.size()).isEqualTo(0); - } - - @Test - public void addPeerReturnsFalseIfSuppliedPeerIsAlreadyInList() throws IOException { - final URL resource = PersistentJsonPeerCacheTest.class.getResource("valid_static_nodes.json"); - final Path original = Paths.get(resource.getPath()); - final File tempFile = testFolder.newFile("file.txt"); - tempFile.deleteOnExit(); - final Path tempPath = tempFile.toPath(); - Files.copy(original, tempPath, StandardCopyOption.REPLACE_EXISTING); - - final PeerCache cache = PersistentJsonPeerCache.fromPath(tempPath); - - final ImmutableList preAddList = cache.getStaticNodes(); - assertThat(cache.add(firstItemInValidList)).isFalse(); - assertThat(cache.getStaticNodes()).containsExactlyElementsOf(preAddList); - } - - @Test - public void addPeerWritesToFileIfPeerIsNotAlreadyInList() throws IOException { - final File tempFile = testFolder.newFile("file.txt"); - tempFile.deleteOnExit(); - final PeerCache cache = PersistentJsonPeerCache.fromPath(tempFile.toPath()); - - assertThat(cache.add(firstItemInValidList)).isTrue(); - assertThat(cache.getStaticNodes()).containsExactly(firstItemInValidList); - - // Ensure file has been updated by reloading into a new cache (ensures file validity as well). - final PersistentJsonPeerCache newCache = PersistentJsonPeerCache.fromPath(tempFile.toPath()); - assertThat(newCache.getStaticNodes()).contains(firstItemInValidList); - } - - @Test - public void removePeerReturnsFalseIfSuppliedPeerIsNotInList() throws IOException { - final File tempFile = testFolder.newFile("file.txt"); - tempFile.deleteOnExit(); - final PeerCache cache = PersistentJsonPeerCache.fromPath(tempFile.toPath()); - - assertThat(cache.remove(firstItemInValidList)).isFalse(); - assertThat(cache.getStaticNodes().size()).isZero(); - } - - @Test - public void removePeerReturnsTrueAndRemovesItemIfPeerIsInList() throws IOException { - final URL resource = PersistentJsonPeerCacheTest.class.getResource("valid_static_nodes.json"); - final Path original = Paths.get(resource.getPath()); - final File tempFile = testFolder.newFile("file.txt"); - tempFile.deleteOnExit(); - final Path tempPath = tempFile.toPath(); - Files.copy(original, tempPath, StandardCopyOption.REPLACE_EXISTING); - - final PeerCache cache = PersistentJsonPeerCache.fromPath(tempPath); - assertThat(cache.remove(firstItemInValidList)).isTrue(); - assertThat(cache.getStaticNodes()).doesNotContain(firstItemInValidList); - - // Ensure file has been updated by reloading into a new cache (ensures file validity as well). - final PersistentJsonPeerCache newCache = PersistentJsonPeerCache.fromPath(tempPath); - assertThat(newCache.getStaticNodes()).doesNotContain(firstItemInValidList); - } -} diff --git a/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/peers/StaticeNodesParserTest.java b/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/peers/StaticeNodesParserTest.java new file mode 100644 index 0000000000..51a2d69a68 --- /dev/null +++ b/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/peers/StaticeNodesParserTest.java @@ -0,0 +1,91 @@ +/* + * Copyright 2019 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.peers; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import io.vertx.core.json.DecodeException; +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Set; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import tech.pegasys.pantheon.util.enode.EnodeURL; + +public class StaticeNodesParserTest { + + // NOTE: The invalid_static_nodes file is identical to the valid, however one node's port is set + // to "A". + + // First peer ion the valid_static_nodes file. + private final EnodeURL firstItemInValidList = + new EnodeURL( + "50203c6bfca6874370e71aecc8958529fd723feb05013dc1abca8fc1fff845c5259faba05852e9dfe5ce172a7d6e7c2a3a5eaa8b541c8af15ea5518bbff5f2fa", + "127.0.0.1", + 30303); + + @Rule public TemporaryFolder testFolder = new TemporaryFolder(); + + @Test + public void validFileLoadsWithExpectedNumberOfPeers() throws IOException { + final URL resource = StaticeNodesParserTest.class.getResource("valid_static_nodes.json"); + final Path path = Paths.get(resource.getPath()); + + final Set enodes = StaticNodesParser.fromPath(path); + + assertThat(enodes.size()).isEqualTo(4); + } + + @Test + public void invalidFileThrowsAnException() { + final URL resource = StaticeNodesParserTest.class.getResource("invalid_static_nodes.json"); + final Path path = Paths.get(resource.getPath()); + + assertThatThrownBy(() -> StaticNodesParser.fromPath(path)) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + public void nonJsonFileThrowsAnException() throws IOException { + final File tempFile = testFolder.newFile("file.txt"); + tempFile.deleteOnExit(); + Files.write(tempFile.toPath(), "This Is Not Json".getBytes(Charset.forName("UTF-8"))); + + assertThatThrownBy(() -> StaticNodesParser.fromPath(tempFile.toPath())) + .isInstanceOf(DecodeException.class); + } + + @Test + public void anEmptyCacheIsCreatedIfTheFileDoesNotExist() throws IOException { + final Path path = Paths.get("./arbirtraryFilename.txt"); + + final Set enodes = StaticNodesParser.fromPath(path); + assertThat(enodes.size()).isZero(); + } + + @Test + public void cacheIsCreatedIfFileExistsButIsEmpty() throws IOException { + final File tempFile = testFolder.newFile("file.txt"); + tempFile.deleteOnExit(); + + final Set enodes = StaticNodesParser.fromPath(tempFile.toPath()); + assertThat(enodes.size()).isZero(); + } +} diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/RunnerBuilder.java b/pantheon/src/main/java/tech/pegasys/pantheon/RunnerBuilder.java index 6aca227aa0..6e54371c22 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/RunnerBuilder.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/RunnerBuilder.java @@ -12,7 +12,17 @@ */ package tech.pegasys.pantheon; +import com.google.common.base.Preconditions; +import io.vertx.core.Vertx; import java.io.IOException; +import java.net.URI; +import java.nio.file.Path; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; import tech.pegasys.pantheon.cli.EthNetworkConfig; import tech.pegasys.pantheon.controller.PantheonController; import tech.pegasys.pantheon.crypto.SECP256K1.KeyPair; @@ -53,9 +63,7 @@ import tech.pegasys.pantheon.ethereum.p2p.peers.DefaultPeer; import tech.pegasys.pantheon.ethereum.p2p.peers.Peer; import tech.pegasys.pantheon.ethereum.p2p.peers.PeerBlacklist; -import tech.pegasys.pantheon.ethereum.p2p.peers.cache.PeerCache; -import tech.pegasys.pantheon.ethereum.p2p.peers.cache.PersistentJsonPeerCache; -import tech.pegasys.pantheon.ethereum.p2p.peers.cache.StaticNodesParser; +import tech.pegasys.pantheon.ethereum.p2p.peers.StaticNodesParser; import tech.pegasys.pantheon.ethereum.p2p.wire.Capability; import tech.pegasys.pantheon.ethereum.p2p.wire.SubProtocol; import tech.pegasys.pantheon.ethereum.permissioning.AccountWhitelistController; @@ -68,18 +76,6 @@ import tech.pegasys.pantheon.util.bytes.BytesValue; import tech.pegasys.pantheon.util.enode.EnodeURL; -import java.net.URI; -import java.nio.file.Path; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; - -import com.google.common.base.Preconditions; -import io.vertx.core.Vertx; - public class RunnerBuilder { private Vertx vertx; @@ -97,6 +93,7 @@ public class RunnerBuilder { private MetricsConfiguration metricsConfiguration; private MetricsSystem metricsSystem; private Optional permissioningConfiguration = Optional.empty(); + private Set staticNodes; private EnodeURL getSelfEnode() { String nodeId = pantheonController.getLocalNodeKeyPair().getPublicKey().toString(); @@ -179,7 +176,12 @@ public RunnerBuilder metricsSystem(final MetricsSystem metricsSystem) { return this; } - public Runner build() throws IOException { + public RunnerBuilder staticNodes(final Set staticNodes) { + this.staticNodes = staticNodes; + return this; + } + + public Runner build() { Preconditions.checkNotNull(pantheonController); @@ -277,7 +279,10 @@ public Runner build() throws IOException { final FilterManager filterManager = createFilterManager(vertx, context, transactionPool); final P2PNetwork peerNetwork = networkRunner.getNetwork(); - addStaticPeersToNetwork(peerNetwork); + staticNodes.stream().forEach(enodeURL -> { + final Peer peer = DefaultPeer.fromEnodeURL(enodeURL); + peerNetwork.addMaintainConnectionPeer(peer); + }); Optional jsonRpcHttpService = Optional.empty(); if (jsonRpcConfiguration.isEnabled()) { @@ -456,20 +461,4 @@ private MetricsService createMetricsService( final Vertx vertx, final MetricsConfiguration configuration) { return MetricsService.create(vertx, configuration, metricsSystem); } - - - private PeerCache addStaticPeersToNetwork(final P2PNetwork peerNetwork) throws IOException { - final String STATIC_NODES_FILENAME = "static_nodes.json"; - final Path staticNodesPath = dataDir.resolve(STATIC_NODES_FILENAME); - - final Set staticPeers = StaticNodesParser.fromPath(staticNodesPath); - - staticPeers.stream().forEach(enodeURL -> { - final Peer peer = DefaultPeer.fromEnodeURL(enodeURL); - peerNetwork.addMaintainConnectionPeer(peer); - });) - - - - } } 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 53eab664d2..ce4a6d4f1a 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java @@ -25,38 +25,6 @@ import static tech.pegasys.pantheon.metrics.prometheus.MetricsConfiguration.DEFAULT_METRICS_PUSH_PORT; import static tech.pegasys.pantheon.metrics.prometheus.MetricsConfiguration.createDefault; -import com.google.common.base.Suppliers; -import com.google.common.io.Resources; -import io.vertx.core.Vertx; -import io.vertx.core.json.DecodeException; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.net.InetAddress; -import java.net.URI; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import java.util.Optional; -import java.util.Set; -import java.util.function.Function; -import java.util.function.Supplier; -import java.util.stream.Collectors; -import java.util.stream.Stream; -import org.apache.logging.log4j.Level; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.apache.logging.log4j.core.config.Configurator; -import picocli.CommandLine; -import picocli.CommandLine.AbstractParseResultHandler; -import picocli.CommandLine.Command; -import picocli.CommandLine.ExecutionException; -import picocli.CommandLine.ITypeConverter; -import picocli.CommandLine.Option; -import picocli.CommandLine.ParameterException; import tech.pegasys.pantheon.Runner; import tech.pegasys.pantheon.RunnerBuilder; import tech.pegasys.pantheon.cli.PublicKeySubCommand.KeyLoader; @@ -80,7 +48,7 @@ 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.peers.cache.PeerCache; +import tech.pegasys.pantheon.ethereum.p2p.peers.StaticNodesParser; import tech.pegasys.pantheon.ethereum.permissioning.LocalPermissioningConfiguration; import tech.pegasys.pantheon.ethereum.permissioning.PermissioningConfigurationBuilder; import tech.pegasys.pantheon.metrics.MetricCategory; @@ -93,6 +61,40 @@ import tech.pegasys.pantheon.util.bytes.BytesValue; import tech.pegasys.pantheon.util.enode.EnodeURL; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.InetAddress; +import java.net.URI; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import com.google.common.base.Suppliers; +import com.google.common.io.Resources; +import io.vertx.core.Vertx; +import io.vertx.core.json.DecodeException; +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.core.config.Configurator; +import picocli.CommandLine; +import picocli.CommandLine.AbstractParseResultHandler; +import picocli.CommandLine.Command; +import picocli.CommandLine.ExecutionException; +import picocli.CommandLine.ITypeConverter; +import picocli.CommandLine.Option; +import picocli.CommandLine.ParameterException; + @SuppressWarnings("FieldCanBeLocal") // because Picocli injected fields report false positives @Command( description = "This command runs the Pantheon Ethereum client full node.", @@ -119,7 +121,7 @@ public RpcApi convert(final String name) throws RpcApisConversionException { final String uppercaseName = name.trim().toUpperCase(); return Stream.>>of( - RpcApis::valueOf, CliqueRpcApis::valueOf, IbftRpcApis::valueOf) + RpcApis::valueOf, CliqueRpcApis::valueOf, IbftRpcApis::valueOf) .map(f -> f.apply(uppercaseName)) .filter(Optional::isPresent) .map(Optional::get) @@ -846,7 +848,8 @@ private void synchronize( final JsonRpcConfiguration jsonRpcConfiguration, final WebSocketConfiguration webSocketConfiguration, final MetricsConfiguration metricsConfiguration, - final Optional permissioningConfiguration) { + final Optional permissioningConfiguration) + throws IOException { checkNotNull(runnerBuilder); @@ -868,6 +871,7 @@ private void synchronize( .bannedNodeIds(bannedNodeIds) .metricsSystem(metricsSystem.get()) .metricsConfiguration(metricsConfiguration) + .staticNodes(loadStaticNodes()) .build(); addShutdownHook(runner); @@ -1086,4 +1090,12 @@ public PantheonExceptionHandler exceptionHandler() { return exceptionHandlerSupplier.get(); } + private Set loadStaticNodes() throws IOException { + final String STATIC_NODES_FILENAME = "static_nodes.json"; + final Path staticNodesPath = dataDir().resolve(STATIC_NODES_FILENAME); + + return StaticNodesParser.fromPath(staticNodesPath); + + } + } diff --git a/pantheon/src/test/java/tech/pegasys/pantheon/RunnerTest.java b/pantheon/src/test/java/tech/pegasys/pantheon/RunnerTest.java index faf621ff79..4ebc2b37f2 100644 --- a/pantheon/src/test/java/tech/pegasys/pantheon/RunnerTest.java +++ b/pantheon/src/test/java/tech/pegasys/pantheon/RunnerTest.java @@ -18,6 +18,30 @@ import static tech.pegasys.pantheon.cli.NetworkName.DEV; import static tech.pegasys.pantheon.controller.KeyPairUtil.loadKeyPair; +import io.vertx.core.Future; +import io.vertx.core.Vertx; +import io.vertx.core.buffer.Buffer; +import io.vertx.core.http.HttpClient; +import io.vertx.core.json.Json; +import io.vertx.core.json.JsonObject; +import java.io.IOException; +import java.net.InetAddress; +import java.net.URI; +import java.nio.file.Path; +import java.time.Duration; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.TimeUnit; +import okhttp3.Call; +import okhttp3.MediaType; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; +import org.awaitility.Awaitility; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; import tech.pegasys.pantheon.cli.EthNetworkConfig; import tech.pegasys.pantheon.config.GenesisConfigFile; import tech.pegasys.pantheon.controller.MainnetPantheonController; @@ -40,8 +64,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.p2p.peers.cache.PeerCache; -import tech.pegasys.pantheon.ethereum.p2p.peers.cache.PersistentJsonPeerCache; import tech.pegasys.pantheon.ethereum.permissioning.LocalPermissioningConfiguration; import tech.pegasys.pantheon.ethereum.storage.StorageProvider; import tech.pegasys.pantheon.ethereum.storage.keyvalue.RocksDbStorageProvider; @@ -51,33 +73,6 @@ import tech.pegasys.pantheon.testutil.TestClock; import tech.pegasys.pantheon.util.uint.UInt256; -import java.io.IOException; -import java.net.InetAddress; -import java.net.URI; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.time.Duration; -import java.util.Collections; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import io.vertx.core.Future; -import io.vertx.core.Vertx; -import io.vertx.core.buffer.Buffer; -import io.vertx.core.http.HttpClient; -import io.vertx.core.json.Json; -import io.vertx.core.json.JsonObject; -import okhttp3.Call; -import okhttp3.MediaType; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.RequestBody; -import okhttp3.Response; -import org.awaitility.Awaitility; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; - /** Tests for {@link Runner}. */ public final class RunnerTest { @@ -142,8 +137,7 @@ private void syncFromGenesis(final SyncMode mode) throws Exception { final MetricsConfiguration aheadMetricsConfiguration = metricsConfiguration(); final LocalPermissioningConfiguration aheadPermissioningConfiguration = permissioningConfiguration(); - final PeerCache peerCache = - new PersistentJsonPeerCache(emptySet(), Paths.get("./arbitraryPath.txt")); + final RunnerBuilder runnerBuilder = new RunnerBuilder() .vertx(Vertx.vertx()) @@ -152,8 +146,7 @@ private void syncFromGenesis(final SyncMode mode) throws Exception { .discoveryPort(0) .maxPeers(3) .metricsSystem(noOpMetricsSystem) - .bannedNodeIds(emptySet()) - .peerCache(peerCache); + .bannedNodeIds(emptySet()); Runner runnerBehind = null; final Runner runnerAhead = 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 69bc411665..f55eaef024 100644 --- a/pantheon/src/test/java/tech/pegasys/pantheon/cli/CommandTestAbstract.java +++ b/pantheon/src/test/java/tech/pegasys/pantheon/cli/CommandTestAbstract.java @@ -124,7 +124,7 @@ public void initMocks() throws Exception { when(mockRunnerBuilder.bannedNodeIds(any())).thenReturn(mockRunnerBuilder); when(mockRunnerBuilder.metricsSystem(any())).thenReturn(mockRunnerBuilder); when(mockRunnerBuilder.metricsConfiguration(any())).thenReturn(mockRunnerBuilder); - when(mockRunnerBuilder.peerCache(any())).thenReturn(mockRunnerBuilder); + when(mockRunnerBuilder.staticNodes(any())).thenReturn(mockRunnerBuilder); when(mockRunnerBuilder.build()).thenReturn(mockRunner); } From 1bfb7f5423d05f890ecbadb784635fc528c65307 Mon Sep 17 00:00:00 2001 From: tmohay Date: Tue, 19 Mar 2019 14:16:53 +1100 Subject: [PATCH 07/11] Updated tests --- .../ethereum/p2p/peers/StaticNodesParser.java | 8 +++++--- .../ethereum/p2p/peers/StaticeNodesParserTest.java | 6 ++++-- .../java/tech/pegasys/pantheon/RunnerBuilder.java | 11 ++++++----- .../tech/pegasys/pantheon/cli/PantheonCommand.java | 7 +++---- .../test/java/tech/pegasys/pantheon/RunnerTest.java | 4 +++- 5 files changed, 21 insertions(+), 15 deletions(-) diff --git a/ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/peers/StaticNodesParser.java b/ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/peers/StaticNodesParser.java index 77cf3427fc..8c85cefbe4 100644 --- a/ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/peers/StaticNodesParser.java +++ b/ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/peers/StaticNodesParser.java @@ -14,8 +14,8 @@ import static java.nio.charset.StandardCharsets.UTF_8; -import io.vertx.core.json.DecodeException; -import io.vertx.core.json.JsonArray; +import tech.pegasys.pantheon.util.enode.EnodeURL; + import java.io.FileNotFoundException; import java.io.IOException; import java.nio.file.Files; @@ -23,9 +23,11 @@ import java.nio.file.Path; import java.util.HashSet; import java.util.Set; + +import io.vertx.core.json.DecodeException; +import io.vertx.core.json.JsonArray; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import tech.pegasys.pantheon.util.enode.EnodeURL; public class StaticNodesParser { diff --git a/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/peers/StaticeNodesParserTest.java b/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/peers/StaticeNodesParserTest.java index 51a2d69a68..5bd40c1bdd 100644 --- a/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/peers/StaticeNodesParserTest.java +++ b/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/peers/StaticeNodesParserTest.java @@ -15,7 +15,8 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; -import io.vertx.core.json.DecodeException; +import tech.pegasys.pantheon.util.enode.EnodeURL; + import java.io.File; import java.io.IOException; import java.net.URL; @@ -24,10 +25,11 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.Set; + +import io.vertx.core.json.DecodeException; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; -import tech.pegasys.pantheon.util.enode.EnodeURL; public class StaticeNodesParserTest { diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/RunnerBuilder.java b/pantheon/src/main/java/tech/pegasys/pantheon/RunnerBuilder.java index 8712e3aaaa..493d871712 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/RunnerBuilder.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/RunnerBuilder.java @@ -52,7 +52,6 @@ import tech.pegasys.pantheon.ethereum.p2p.peers.DefaultPeer; import tech.pegasys.pantheon.ethereum.p2p.peers.Peer; import tech.pegasys.pantheon.ethereum.p2p.peers.PeerBlacklist; -import tech.pegasys.pantheon.ethereum.p2p.peers.StaticNodesParser; import tech.pegasys.pantheon.ethereum.p2p.wire.Capability; import tech.pegasys.pantheon.ethereum.p2p.wire.SubProtocol; import tech.pegasys.pantheon.ethereum.permissioning.AccountWhitelistController; @@ -300,10 +299,12 @@ public Runner build() { final FilterManager filterManager = createFilterManager(vertx, context, transactionPool); final P2PNetwork peerNetwork = networkRunner.getNetwork(); - staticNodes.stream().forEach(enodeURL -> { - final Peer peer = DefaultPeer.fromEnodeURL(enodeURL); - peerNetwork.addMaintainConnectionPeer(peer); - }); + staticNodes.stream() + .forEach( + enodeURL -> { + final Peer peer = DefaultPeer.fromEnodeURL(enodeURL); + peerNetwork.addMaintainConnectionPeer(peer); + }); Optional jsonRpcHttpService = Optional.empty(); if (jsonRpcConfiguration.isEnabled()) { 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 c81927da17..f60558c2cc 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java @@ -910,7 +910,8 @@ private void synchronize( final JsonRpcConfiguration jsonRpcConfiguration, final WebSocketConfiguration webSocketConfiguration, final MetricsConfiguration metricsConfiguration, - final Optional permissioningConfiguration) { + final Optional permissioningConfiguration) + throws IOException { checkNotNull(runnerBuilder); @@ -1165,11 +1166,9 @@ public PantheonExceptionHandler exceptionHandler() { } private Set loadStaticNodes() throws IOException { - final String STATIC_NODES_FILENAME = "static_nodes.json"; + final String STATIC_NODES_FILENAME = "static-nodes.json"; final Path staticNodesPath = dataDir().resolve(STATIC_NODES_FILENAME); return StaticNodesParser.fromPath(staticNodesPath); - } - } diff --git a/pantheon/src/test/java/tech/pegasys/pantheon/RunnerTest.java b/pantheon/src/test/java/tech/pegasys/pantheon/RunnerTest.java index a567d3e658..689d203d10 100644 --- a/pantheon/src/test/java/tech/pegasys/pantheon/RunnerTest.java +++ b/pantheon/src/test/java/tech/pegasys/pantheon/RunnerTest.java @@ -12,6 +12,7 @@ */ package tech.pegasys.pantheon; +import static java.util.Collections.emptySet; import static org.assertj.core.api.Assertions.assertThat; import static tech.pegasys.pantheon.cli.EthNetworkConfig.DEV_NETWORK_ID; import static tech.pegasys.pantheon.cli.NetworkName.DEV; @@ -143,7 +144,8 @@ private void syncFromGenesis(final SyncMode mode) throws Exception { .discoveryPort(0) .maxPeers(3) .metricsSystem(noOpMetricsSystem) - .bannedNodeIds(Collections.emptySet()); + .bannedNodeIds(emptySet()) + .staticNodes(emptySet()); Runner runnerBehind = null; final Runner runnerAhead = From 6e898f75162d31acd025f30e94cabec26843478c Mon Sep 17 00:00:00 2001 From: tmohay Date: Tue, 19 Mar 2019 15:21:30 +1100 Subject: [PATCH 08/11] post review --- .../p2p/peers/StaticeNodesParserTest.java | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/peers/StaticeNodesParserTest.java b/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/peers/StaticeNodesParserTest.java index 5bd40c1bdd..c9cf3b8fcd 100644 --- a/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/peers/StaticeNodesParserTest.java +++ b/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/peers/StaticeNodesParserTest.java @@ -15,6 +15,8 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import com.google.common.collect.Lists; +import java.util.List; import tech.pegasys.pantheon.util.enode.EnodeURL; import java.io.File; @@ -37,22 +39,34 @@ public class StaticeNodesParserTest { // to "A". // First peer ion the valid_static_nodes file. - private final EnodeURL firstItemInValidList = + private final List validFileItems = Lists.newArrayList( new EnodeURL( "50203c6bfca6874370e71aecc8958529fd723feb05013dc1abca8fc1fff845c5259faba05852e9dfe5ce172a7d6e7c2a3a5eaa8b541c8af15ea5518bbff5f2fa", "127.0.0.1", - 30303); + 30303), + new EnodeURL( + "02beb46bc17227616be44234071dfa18516684e45eed88049190b6cb56b0bae218f045fd0450f123b8f55c60b96b78c45e8e478004293a8de6818aa4e02eff97", + "127.0.0.1", + 30304), + new EnodeURL( + "819e5cbd81f123516b10f04bf620daa2b385efef06d77253148b814bf1bb6197ff58ebd1fd7bf5dc765b49a4440c733bf941e479c800173f2bfeb887e4fbcbc2", + "127.0.0.1", + 30305), + new EnodeURL( + "6cf53e25d2a98a22e7e205a86bda7077e3c8a7bc99e5ff88ddfd2037a550969ab566f069ffa455df0cfae0c21f7aec3447e414eccc473a3e8b20984b90f164ac", + "127.0.0.1", + 30306)); @Rule public TemporaryFolder testFolder = new TemporaryFolder(); @Test - public void validFileLoadsWithExpectedNumberOfPeers() throws IOException { + public void validFileLoadsWithExpectedEnodes() throws IOException { final URL resource = StaticeNodesParserTest.class.getResource("valid_static_nodes.json"); final Path path = Paths.get(resource.getPath()); final Set enodes = StaticNodesParser.fromPath(path); - assertThat(enodes.size()).isEqualTo(4); + assertThat(enodes).containsExactly(validFileItems.toArray(new EnodeURL[validFileItems.size()])); } @Test From b0482689d5a43fbe89397e12c0a3f617e7a8ab62 Mon Sep 17 00:00:00 2001 From: tmohay Date: Tue, 19 Mar 2019 16:24:54 +1100 Subject: [PATCH 09/11] fix spotless --- .../p2p/peers/StaticeNodesParserTest.java | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/peers/StaticeNodesParserTest.java b/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/peers/StaticeNodesParserTest.java index c9cf3b8fcd..47dd5c72fa 100644 --- a/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/peers/StaticeNodesParserTest.java +++ b/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/peers/StaticeNodesParserTest.java @@ -15,8 +15,6 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; -import com.google.common.collect.Lists; -import java.util.List; import tech.pegasys.pantheon.util.enode.EnodeURL; import java.io.File; @@ -26,8 +24,10 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.List; import java.util.Set; +import com.google.common.collect.Lists; import io.vertx.core.json.DecodeException; import org.junit.Rule; import org.junit.Test; @@ -39,21 +39,22 @@ public class StaticeNodesParserTest { // to "A". // First peer ion the valid_static_nodes file. - private final List validFileItems = Lists.newArrayList( - new EnodeURL( - "50203c6bfca6874370e71aecc8958529fd723feb05013dc1abca8fc1fff845c5259faba05852e9dfe5ce172a7d6e7c2a3a5eaa8b541c8af15ea5518bbff5f2fa", - "127.0.0.1", - 30303), - new EnodeURL( - "02beb46bc17227616be44234071dfa18516684e45eed88049190b6cb56b0bae218f045fd0450f123b8f55c60b96b78c45e8e478004293a8de6818aa4e02eff97", + private final List validFileItems = + Lists.newArrayList( + new EnodeURL( + "50203c6bfca6874370e71aecc8958529fd723feb05013dc1abca8fc1fff845c5259faba05852e9dfe5ce172a7d6e7c2a3a5eaa8b541c8af15ea5518bbff5f2fa", + "127.0.0.1", + 30303), + new EnodeURL( + "02beb46bc17227616be44234071dfa18516684e45eed88049190b6cb56b0bae218f045fd0450f123b8f55c60b96b78c45e8e478004293a8de6818aa4e02eff97", "127.0.0.1", 30304), - new EnodeURL( - "819e5cbd81f123516b10f04bf620daa2b385efef06d77253148b814bf1bb6197ff58ebd1fd7bf5dc765b49a4440c733bf941e479c800173f2bfeb887e4fbcbc2", + new EnodeURL( + "819e5cbd81f123516b10f04bf620daa2b385efef06d77253148b814bf1bb6197ff58ebd1fd7bf5dc765b49a4440c733bf941e479c800173f2bfeb887e4fbcbc2", "127.0.0.1", 30305), - new EnodeURL( - "6cf53e25d2a98a22e7e205a86bda7077e3c8a7bc99e5ff88ddfd2037a550969ab566f069ffa455df0cfae0c21f7aec3447e414eccc473a3e8b20984b90f164ac", + new EnodeURL( + "6cf53e25d2a98a22e7e205a86bda7077e3c8a7bc99e5ff88ddfd2037a550969ab566f069ffa455df0cfae0c21f7aec3447e414eccc473a3e8b20984b90f164ac", "127.0.0.1", 30306)); From 0eeafb42aef351706509b418f8e702050792b12a Mon Sep 17 00:00:00 2001 From: tmohay Date: Tue, 19 Mar 2019 19:38:57 +1100 Subject: [PATCH 10/11] post review --- .../ethereum/p2p/peers/StaticNodesParser.java | 17 +++++++---------- ...rserTest.java => StaticNodesParserTest.java} | 6 +++--- .../pegasys/pantheon/cli/PantheonCommand.java | 4 ++-- 3 files changed, 12 insertions(+), 15 deletions(-) rename ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/peers/{StaticeNodesParserTest.java => StaticNodesParserTest.java} (94%) diff --git a/ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/peers/StaticNodesParser.java b/ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/peers/StaticNodesParser.java index 8c85cefbe4..98c2b16786 100644 --- a/ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/peers/StaticNodesParser.java +++ b/ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/peers/StaticNodesParser.java @@ -13,6 +13,7 @@ package tech.pegasys.pantheon.ethereum.p2p.peers; import static java.nio.charset.StandardCharsets.UTF_8; +import static java.util.Collections.emptySet; import tech.pegasys.pantheon.util.enode.EnodeURL; @@ -21,8 +22,8 @@ import java.nio.file.Files; import java.nio.file.NoSuchFileException; import java.nio.file.Path; -import java.util.HashSet; import java.util.Set; +import java.util.stream.Collectors; import io.vertx.core.json.DecodeException; import io.vertx.core.json.JsonArray; @@ -35,17 +36,16 @@ public class StaticNodesParser { public static Set fromPath(final Path path) throws IOException, IllegalArgumentException { - final Set result = new HashSet<>(); final byte[] staticNodesContent; try { staticNodesContent = Files.readAllBytes(path); if (staticNodesContent.length == 0) { - return result; + return emptySet(); } } catch (FileNotFoundException | NoSuchFileException ex) { LOG.info("No StaticNodes file ({}) exists, creating empty cache.", path); - return result; + return emptySet(); } catch (IOException ex) { LOG.info("Unable to parse static nodes file ({})", path); throw ex; @@ -53,10 +53,9 @@ public static Set fromPath(final Path path) try { final JsonArray enodeJsonArray = new JsonArray(new String(staticNodesContent, UTF_8)); - for (Object jsonObj : enodeJsonArray.getList()) { - final String enodeString = (String) jsonObj; - result.add(decodeString(enodeString)); - } + return enodeJsonArray.stream() + .map(obj -> decodeString((String) obj)) + .collect(Collectors.toSet()); } catch (DecodeException ex) { LOG.info("Content of ({}} was invalid json, and could not be decoded.", path); throw ex; @@ -64,8 +63,6 @@ public static Set fromPath(final Path path) LOG.info("Parsing ({}) has failed due incorrectly formatted enode element.", path); throw ex; } - - return result; } private static EnodeURL decodeString(final String input) { diff --git a/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/peers/StaticeNodesParserTest.java b/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/peers/StaticNodesParserTest.java similarity index 94% rename from ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/peers/StaticeNodesParserTest.java rename to ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/peers/StaticNodesParserTest.java index 47dd5c72fa..5b6cc53523 100644 --- a/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/peers/StaticeNodesParserTest.java +++ b/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/peers/StaticNodesParserTest.java @@ -33,7 +33,7 @@ import org.junit.Test; import org.junit.rules.TemporaryFolder; -public class StaticeNodesParserTest { +public class StaticNodesParserTest { // NOTE: The invalid_static_nodes file is identical to the valid, however one node's port is set // to "A". @@ -62,7 +62,7 @@ public class StaticeNodesParserTest { @Test public void validFileLoadsWithExpectedEnodes() throws IOException { - final URL resource = StaticeNodesParserTest.class.getResource("valid_static_nodes.json"); + final URL resource = StaticNodesParserTest.class.getResource("valid_static_nodes.json"); final Path path = Paths.get(resource.getPath()); final Set enodes = StaticNodesParser.fromPath(path); @@ -72,7 +72,7 @@ public void validFileLoadsWithExpectedEnodes() throws IOException { @Test public void invalidFileThrowsAnException() { - final URL resource = StaticeNodesParserTest.class.getResource("invalid_static_nodes.json"); + final URL resource = StaticNodesParserTest.class.getResource("invalid_static_nodes.json"); final Path path = Paths.get(resource.getPath()); assertThatThrownBy(() -> StaticNodesParser.fromPath(path)) 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 f60558c2cc..93e8d97d77 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java @@ -1166,8 +1166,8 @@ public PantheonExceptionHandler exceptionHandler() { } private Set loadStaticNodes() throws IOException { - final String STATIC_NODES_FILENAME = "static-nodes.json"; - final Path staticNodesPath = dataDir().resolve(STATIC_NODES_FILENAME); + final String staticNodesFilname = "static-nodes.json"; + final Path staticNodesPath = dataDir().resolve(staticNodesFilname); return StaticNodesParser.fromPath(staticNodesPath); } From d222a3a84fadec2e97bb7ba42772f499adf6580e Mon Sep 17 00:00:00 2001 From: tmohay Date: Wed, 20 Mar 2019 11:20:36 +1100 Subject: [PATCH 11/11] fix JFrame questions --- .../ethereum/p2p/peers/StaticNodesParser.java | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/peers/StaticNodesParser.java b/ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/peers/StaticNodesParser.java index 98c2b16786..faeca524f1 100644 --- a/ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/peers/StaticNodesParser.java +++ b/ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/peers/StaticNodesParser.java @@ -37,25 +37,14 @@ public class StaticNodesParser { public static Set fromPath(final Path path) throws IOException, IllegalArgumentException { - final byte[] staticNodesContent; try { - staticNodesContent = Files.readAllBytes(path); - if (staticNodesContent.length == 0) { - return emptySet(); - } + return readEnodesFromPath(path); } catch (FileNotFoundException | NoSuchFileException ex) { LOG.info("No StaticNodes file ({}) exists, creating empty cache.", path); return emptySet(); } catch (IOException ex) { LOG.info("Unable to parse static nodes file ({})", path); throw ex; - } - - try { - final JsonArray enodeJsonArray = new JsonArray(new String(staticNodesContent, UTF_8)); - return enodeJsonArray.stream() - .map(obj -> decodeString((String) obj)) - .collect(Collectors.toSet()); } catch (DecodeException ex) { LOG.info("Content of ({}} was invalid json, and could not be decoded.", path); throw ex; @@ -65,6 +54,18 @@ public static Set fromPath(final Path path) } } + private static Set readEnodesFromPath(final Path path) throws IOException { + final byte[] staticNodesContent = Files.readAllBytes(path); + if (staticNodesContent.length == 0) { + return emptySet(); + } + + final JsonArray enodeJsonArray = new JsonArray(new String(staticNodesContent, UTF_8)); + return enodeJsonArray.stream() + .map(obj -> decodeString((String) obj)) + .collect(Collectors.toSet()); + } + private static EnodeURL decodeString(final String input) { try { return new EnodeURL(input);