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

[NC-1970] admin_addPeer #622

Merged
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import tech.pegasys.pantheon.ethereum.core.Synchronizer;
import tech.pegasys.pantheon.ethereum.core.TransactionPool;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.filter.FilterManager;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.AdminAddPeer;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.AdminPeers;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.DebugMetrics;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.DebugStorageRangeAt;
Expand Down Expand Up @@ -238,6 +239,7 @@ blockchainQueries, new TransactionTracer(blockReplay), parameter),
}
if (rpcApis.contains(RpcApis.ADMIN)) {
addMethods(enabledMethods, new AdminPeers(p2pNetwork));
addMethods(enabledMethods, new AdminAddPeer(p2pNetwork, parameter));
}
if (rpcApis.contains(RpcApis.PERM)) {
addMethods(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* Copyright 2018 ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods;

import tech.pegasys.pantheon.ethereum.jsonrpc.internal.JsonRpcRequest;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.exception.InvalidJsonRpcParameters;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.parameters.JsonRpcParameter;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcError;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcErrorResponse;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcResponse;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcSuccessResponse;
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 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;

public AdminAddPeer(final P2PNetwork peerNetwork, final JsonRpcParameter parameters) {
this.peerNetwork = peerNetwork;
this.parameters = parameters;
}

@Override
public String getName() {
return "admin_addPeer";
}

@Override
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);
} catch (final InvalidJsonRpcParameters e) {
return new JsonRpcErrorResponse(req.getId(), JsonRpcError.INVALID_PARAMS);
} catch (final IllegalArgumentException e) {
return new JsonRpcErrorResponse(req.getId(), JsonRpcError.PARSE_ERROR);
} catch (final Exception e) {
LOG.error("Error processing request: " + req, e);
throw e;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/*
* 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.jsonrpc.internal.methods;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;

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;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcErrorResponse;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcResponse;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcSuccessResponse;
import tech.pegasys.pantheon.ethereum.p2p.api.P2PNetwork;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;

@RunWith(MockitoJUnitRunner.StrictStubs.class)
public class AdminAddPeerTest {

@Mock private P2PNetwork p2pNetwork;
private final JsonRpcParameter parameter = new JsonRpcParameter();

private AdminAddPeer method;

@Before
public void setup() {
method = new AdminAddPeer(p2pNetwork, parameter);
}

@Test
public void requestIsMissingParameter() {
final JsonRpcRequest request = new JsonRpcRequest("2.0", "admin_addPeer", new String[] {});
final JsonRpcResponse expectedResponse =
new JsonRpcErrorResponse(request.getId(), JsonRpcError.INVALID_PARAMS);

final JsonRpcResponse actualResponse = method.response(request);

assertThat(actualResponse).isEqualToComparingFieldByField(expectedResponse);
}

@Test
public void requestHasInvalidEnode() {
final JsonRpcRequest request =
new JsonRpcRequest("2.0", "admin_addPeer", new String[] {"asdf"});
final JsonRpcResponse expectedResponse =
new JsonRpcErrorResponse(request.getId(), JsonRpcError.PARSE_ERROR);

final JsonRpcResponse actualResponse = method.response(request);

assertThat(actualResponse).isEqualToComparingFieldByField(expectedResponse);
}

@Test
public void requestAddsValidEnode() {
when(p2pNetwork.addMaintainConnectionPeer(any())).thenReturn(true);

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(request.getId(), true);

final JsonRpcResponse actualResponse = method.response(request);

assertThat(actualResponse).isEqualToComparingFieldByField(expectedResponse);
}

@Test
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(request.getId(), false);

final JsonRpcResponse actualResponse = method.response(request);

assertThat(actualResponse).isEqualToComparingFieldByField(expectedResponse);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,14 @@ public void subscribeDisconnect(final DisconnectCallback callback) {
disconnectCallbacks.subscribe(callback);
}

@Override
public boolean addMaintainConnectionPeer(final Peer peer) {
return true;
}

@Override
public void checkMaintainedConnectionPeers() {}

@Override
public void stop() {}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Function;
Expand All @@ -50,6 +51,8 @@ public class NetworkRunner implements AutoCloseable {
private final ExecutorService networkExecutor =
Executors.newFixedThreadPool(
1, new ThreadFactoryBuilder().setNameFormat(this.getClass().getSimpleName()).build());
private final ScheduledExecutorService networkCheckExecutor =
Executors.newSingleThreadScheduledExecutor();

private final P2PNetwork network;
private final Map<String, SubProtocol> subProtocols;
Expand Down Expand Up @@ -87,6 +90,7 @@ public void start() {
LOG.info("Starting Network.");
setupHandlers();
networkExecutor.submit(network);
networkCheckExecutor.schedule(network::checkMaintainedConnectionPeers, 60, TimeUnit.SECONDS);
} else {
LOG.error("Attempted to start already running network.");
}
Expand All @@ -100,6 +104,7 @@ public void stop() {
protocolManager.stop();
}
networkExecutor.shutdown();
networkCheckExecutor.shutdown();
shutdown.countDown();
} else {
LOG.error("Attempted to stop already stopped network.");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,21 @@ public interface P2PNetwork extends Closeable, Runnable {
*/
void subscribeDisconnect(DisconnectCallback consumer);

/**
* Adds a {@link Peer} to a list indicating efforts should be made to always stay connected to it
*
* @param peer
* @return boolean representing whether or not the peer has been added to the list or was already
* on it
*/
boolean addMaintainConnectionPeer(final Peer peer);

/**
* Trigger that an external clock can use to make the network attempt connections to maintained
* peers
*/
void checkMaintainedConnectionPeers();

/** Stops the P2P network layer. */
void stop();

Expand Down
Loading