Skip to content
This repository has been archived by the owner on Sep 26, 2019. It is now read-only.

Commit

Permalink
Maintain a staticnodes.json
Browse files Browse the repository at this point in the history
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).
  • Loading branch information
tmohay committed Mar 15, 2019
1 parent b5d025c commit 2111f0e
Show file tree
Hide file tree
Showing 20 changed files with 505 additions and 95 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -85,6 +86,7 @@ public Map<String, JsonRpcMethod> methods() {
final Optional<AccountWhitelistController> accountWhitelistController =
Optional.of(mock(AccountWhitelistController.class));
final PrivacyParameters privacyParameters = mock(PrivacyParameters.class);
final PeerCache peerCache = mock(PeerCache.class);

return new JsonRpcMethodsFactory()
.methods(
Expand All @@ -102,6 +104,7 @@ public Map<String, JsonRpcMethod> methods() {
new HashSet<>(),
accountWhitelistController,
RpcApis.DEFAULT_JSON_RPC_APIS,
privacyParameters);
privacyParameters,
peerCache);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -124,7 +125,8 @@ public Map<String, JsonRpcMethod> methods(
final Collection<RpcApi> rpcApis,
final FilterManager filterManager,
final Optional<AccountWhitelistController> accountsWhitelistController,
final PrivacyParameters privacyParameters) {
final PrivacyParameters privacyParameters,
final PeerCache peerCache) {
final BlockchainQueries blockchainQueries =
new BlockchainQueries(blockchain, worldStateArchive);
return methods(
Expand All @@ -142,7 +144,8 @@ public Map<String, JsonRpcMethod> methods(
supportedCapabilities,
accountsWhitelistController,
rpcApis,
privacyParameters);
privacyParameters,
peerCache);
}

public Map<String, JsonRpcMethod> methods(
Expand All @@ -160,7 +163,8 @@ public Map<String, JsonRpcMethod> methods(
final Set<Capability> supportedCapabilities,
final Optional<AccountWhitelistController> accountsWhitelistController,
final Collection<RpcApi> rpcApis,
final PrivacyParameters privacyParameters) {
final PrivacyParameters privacyParameters,
final PeerCache peerCache) {
final Map<String, JsonRpcMethod> enabledMethods = new HashMap<>();
if (!rpcApis.isEmpty()) {
addMethods(enabledMethods, new RpcModules(rpcApis));
Expand Down Expand Up @@ -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));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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
Expand Down Expand Up @@ -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);
Expand All @@ -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);
}
Expand All @@ -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);
}
Expand All @@ -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);
}
Expand Down
Loading

0 comments on commit 2111f0e

Please sign in to comment.