[PAN-3140] Expand p2p config options (#1940)

Signed-off-by: Adrian Sutton <adrian.sutton@consensys.net>
pull/2/head
mbaxter 5 years ago committed by GitHub
parent 995ef1adba
commit 684a01e202
  1. 3
      ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/config/DiscoveryConfiguration.java
  2. 3
      ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/config/RlpxConfiguration.java
  3. 1
      ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/discovery/PeerDiscoveryAgent.java
  4. 13
      pantheon/src/main/java/tech/pegasys/pantheon/RunnerBuilder.java
  5. 64
      pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java
  6. 6
      pantheon/src/test/java/tech/pegasys/pantheon/cli/CommandTestAbstract.java
  7. 17
      pantheon/src/test/java/tech/pegasys/pantheon/cli/PantheonCommandTest.java
  8. 1
      pantheon/src/test/resources/everything_config.toml
  9. 21
      util/src/main/java/tech/pegasys/pantheon/util/NetworkUtility.java

@ -15,6 +15,7 @@ package tech.pegasys.pantheon.ethereum.p2p.config;
import static java.util.stream.Collectors.toList;
import tech.pegasys.pantheon.ethereum.p2p.peers.EnodeURL;
import tech.pegasys.pantheon.util.NetworkUtility;
import java.util.ArrayList;
import java.util.Collections;
@ -78,7 +79,7 @@ public class DiscoveryConfiguration {
.collect(toList()));
private boolean active = true;
private String bindHost = "0.0.0.0";
private String bindHost = NetworkUtility.INADDR_ANY;
private int bindPort = 30303;
private String advertisedHost = "127.0.0.1";
private int bucketSize = 16;

@ -15,6 +15,7 @@ package tech.pegasys.pantheon.ethereum.p2p.config;
import static com.google.common.base.Preconditions.checkState;
import tech.pegasys.pantheon.ethereum.p2p.rlpx.wire.SubProtocol;
import tech.pegasys.pantheon.util.NetworkUtility;
import java.util.Arrays;
import java.util.Collections;
@ -24,7 +25,7 @@ import java.util.Objects;
public class RlpxConfiguration {
public static final float DEFAULT_FRACTION_REMOTE_CONNECTIONS_ALLOWED = 0.6f;
private String clientId = "TestClient/1.0.0";
private String bindHost = "0.0.0.0";
private String bindHost = NetworkUtility.INADDR_ANY;
private int bindPort = 30303;
private int maxPeers = 25;
private boolean limitRemoteWireConnectionsEnabled = false;

@ -157,6 +157,7 @@ public abstract class PeerDiscoveryAgent {
.build());
this.localNode = Optional.of(ourNode);
isActive = true;
LOG.info("P2P peer discovery agent started and listening on {}", localAddress);
startController(ourNode);
return discoveryPort;
});

@ -12,6 +12,9 @@
*/
package tech.pegasys.pantheon;
import static com.google.common.base.Preconditions.checkArgument;
import static java.util.Objects.isNull;
import tech.pegasys.pantheon.cli.config.EthNetworkConfig;
import tech.pegasys.pantheon.controller.PantheonController;
import tech.pegasys.pantheon.crypto.SECP256K1.KeyPair;
@ -81,6 +84,7 @@ import tech.pegasys.pantheon.metrics.prometheus.MetricsConfiguration;
import tech.pegasys.pantheon.metrics.prometheus.MetricsService;
import tech.pegasys.pantheon.nat.NatMethod;
import tech.pegasys.pantheon.nat.upnp.UpnpNatManager;
import tech.pegasys.pantheon.util.NetworkUtility;
import tech.pegasys.pantheon.util.bytes.BytesValue;
import java.io.IOException;
@ -109,6 +113,7 @@ public class RunnerBuilder {
private boolean p2pEnabled = true;
private boolean discovery;
private String p2pAdvertisedHost;
private String p2pListenInterface = NetworkUtility.INADDR_ANY;
private int p2pListenPort;
private NatMethod natMethod = NatMethod.NONE;
private int maxPeers;
@ -161,6 +166,12 @@ public class RunnerBuilder {
return this;
}
public RunnerBuilder p2pListenInterface(final String ip) {
checkArgument(!isNull(ip), "Invalid null value supplied for p2pListenInterface");
this.p2pListenInterface = ip;
return this;
}
public RunnerBuilder p2pListenPort(final int p2pListenPort) {
this.p2pListenPort = p2pListenPort;
return this;
@ -248,6 +259,7 @@ public class RunnerBuilder {
}
discoveryConfiguration =
DiscoveryConfiguration.create()
.setBindHost(p2pListenInterface)
.setBindPort(p2pListenPort)
.setAdvertisedHost(p2pAdvertisedHost)
.setBootnodes(bootstrap);
@ -272,6 +284,7 @@ public class RunnerBuilder {
final RlpxConfiguration rlpxConfiguration =
RlpxConfiguration.create()
.setBindHost(p2pListenInterface)
.setBindPort(p2pListenPort)
.setMaxPeers(maxPeers)
.setSupportedProtocols(subProtocols)

@ -103,6 +103,7 @@ import tech.pegasys.pantheon.services.PantheonEventsImpl;
import tech.pegasys.pantheon.services.PantheonPluginContextImpl;
import tech.pegasys.pantheon.services.PicoCLIOptionsImpl;
import tech.pegasys.pantheon.services.StorageServiceImpl;
import tech.pegasys.pantheon.util.NetworkUtility;
import tech.pegasys.pantheon.util.PermissioningConfigurationValidator;
import tech.pegasys.pantheon.util.bytes.BytesValue;
import tech.pegasys.pantheon.util.number.Fraction;
@ -114,7 +115,9 @@ import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.URI;
import java.net.UnknownHostException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.Clock;
@ -322,6 +325,14 @@ public class PantheonCommand implements DefaultCommandValues, Runnable {
arity = "1")
private String p2pHost = autoDiscoverDefaultIP().getHostAddress();
@Option(
names = {"--p2p-interface"},
paramLabel = MANDATORY_HOST_FORMAT_HELP,
description =
"The network interface address on which this node listens for p2p communication (default: ${DEFAULT-VALUE})",
arity = "1")
private String p2pInterface = NetworkUtility.INADDR_ANY;
@Option(
names = {"--p2p-port"},
paramLabel = MANDATORY_PORT_FORMAT_HELP,
@ -750,7 +761,7 @@ public class PantheonCommand implements DefaultCommandValues, Runnable {
try {
prepareLogging();
logger.info("Starting Pantheon version: {}", PantheonInfo.version());
checkOptions().configure().controller().startPlugins().startSynchronization();
validateOptions().configure().controller().startPlugins().startSynchronization();
} catch (final Exception e) {
throw new ParameterException(this.commandLine, e.getMessage(), e);
}
@ -862,6 +873,7 @@ public class PantheonCommand implements DefaultCommandValues, Runnable {
ethNetworkConfig,
maxPeers,
p2pHost,
p2pInterface,
p2pPort,
graphQLConfiguration,
jsonRpcConfiguration,
@ -891,8 +903,38 @@ public class PantheonCommand implements DefaultCommandValues, Runnable {
}
}
private PantheonCommand checkOptions() {
// Check that P2P options are able to work or send an error
private PantheonCommand validateOptions() {
issueOptionWarnings();
validateP2PInterface(p2pInterface);
validateMiningParams();
return this;
}
private void validateMiningParams() {
// noinspection ConstantConditions
if (isMiningEnabled && coinbase == null) {
throw new ParameterException(
this.commandLine,
"Unable to mine without a valid coinbase. Either disable mining (remove --miner-enabled)"
+ "or specify the beneficiary of mining (via --miner-coinbase <Address>)");
}
}
protected void validateP2PInterface(final String p2pInterface) {
final String failMessage = "The provided --p2p-interface is not available: " + p2pInterface;
try {
if (!NetworkUtility.isNetworkInterfaceAvailable(p2pInterface)) {
throw new ParameterException(commandLine, failMessage);
}
} catch (UnknownHostException | SocketException e) {
throw new ParameterException(commandLine, failMessage, e);
}
}
private void issueOptionWarnings() {
// Check that P2P options are able to work
checkOptionDependencies(
logger,
commandLine,
@ -904,8 +946,11 @@ public class PantheonCommand implements DefaultCommandValues, Runnable {
"--max-peers",
"--banned-node-id",
"--banned-node-ids",
"--p2p-host",
"--p2p-interface",
"--p2p-port",
"--remote-connections-max-percentage"));
// Check that mining options are able to work or send an error
// Check that mining options are able to work
checkOptionDependencies(
logger,
commandLine,
@ -926,15 +971,6 @@ public class PantheonCommand implements DefaultCommandValues, Runnable {
"--pruning-enabled",
!isPruningEnabled,
asList("--pruning-block-confirmations", "--pruning-blocks-retained"));
// noinspection ConstantConditions
if (isMiningEnabled && coinbase == null) {
throw new ParameterException(
this.commandLine,
"Unable to mine without a valid coinbase. Either disable mining (remove --miner-enabled)"
+ "or specify the beneficiary of mining (via --miner-coinbase <Address>)");
}
return this;
}
private PantheonCommand configure() throws Exception {
@ -1302,6 +1338,7 @@ public class PantheonCommand implements DefaultCommandValues, Runnable {
final EthNetworkConfig ethNetworkConfig,
final int maxPeers,
final String p2pAdvertisedHost,
final String p2pListenInterface,
final int p2pListenPort,
final GraphQLConfiguration graphQLConfiguration,
final JsonRpcConfiguration jsonRpcConfiguration,
@ -1324,6 +1361,7 @@ public class PantheonCommand implements DefaultCommandValues, Runnable {
.discovery(peerDiscoveryEnabled)
.ethNetworkConfig(ethNetworkConfig)
.p2pAdvertisedHost(p2pAdvertisedHost)
.p2pListenInterface(p2pListenInterface)
.p2pListenPort(p2pListenPort)
.maxPeers(maxPeers)
.limitRemoteWireConnectionsEnabled(isLimitRemoteWireConnectionsEnabled)

@ -184,6 +184,7 @@ public abstract class CommandTestAbstract {
when(mockRunnerBuilder.networkingConfiguration(any())).thenReturn(mockRunnerBuilder);
when(mockRunnerBuilder.p2pAdvertisedHost(anyString())).thenReturn(mockRunnerBuilder);
when(mockRunnerBuilder.p2pListenPort(anyInt())).thenReturn(mockRunnerBuilder);
when(mockRunnerBuilder.p2pListenInterface(anyString())).thenReturn(mockRunnerBuilder);
when(mockRunnerBuilder.maxPeers(anyInt())).thenReturn(mockRunnerBuilder);
when(mockRunnerBuilder.limitRemoteWireConnectionsEnabled(anyBoolean()))
.thenReturn(mockRunnerBuilder);
@ -306,6 +307,11 @@ public abstract class CommandTestAbstract {
this.keyLoader = keyLoader;
}
@Override
protected void validateP2PInterface(final String p2pInterface) {
// For testing, don't actually query for networking interfaces to validate this option
}
public CommandSpec getSpec() {
return spec;
}

@ -1179,7 +1179,7 @@ public class PantheonCommandTest extends CommandTestAbstract {
}
@Test
public void p2pHostAndPortOptionMustBeUsed() {
public void p2pHostAndPortOptionsAreRespected() {
final String host = "1.2.3.4";
final int port = 1234;
@ -1196,6 +1196,21 @@ public class PantheonCommandTest extends CommandTestAbstract {
assertThat(commandErrorOutput.toString()).isEmpty();
}
@Test
public void p2pInterfaceOptionIsRespected() {
final String ip = "1.2.3.4";
parseCommand("--p2p-interface", ip);
assertThat(commandOutput.toString()).isEmpty();
assertThat(commandErrorOutput.toString()).isEmpty();
verify(mockRunnerBuilder).p2pListenInterface(stringArgumentCaptor.capture());
verify(mockRunnerBuilder).build();
assertThat(stringArgumentCaptor.getValue()).isEqualTo(ip);
}
@Test
public void p2pHostMayBeLocalhost() {

@ -25,6 +25,7 @@ bootnodes=[
banned-node-ids=["0x6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0","0x6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0"]
banned-node-id=["0x6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0"]
p2p-host="1.2.3.4"
p2p-interface="0.0.0.0"
p2p-port=1234
max-peers=42
remote-connections-limit-enabled=true

@ -16,12 +16,16 @@ import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Enumeration;
import java.util.function.Supplier;
import com.google.common.base.Suppliers;
public class NetworkUtility {
public static final String INADDR_ANY = "0.0.0.0";
public static final String INADDR6_ANY = "0:0:0:0:0:0:0:0";
private NetworkUtility() {}
@ -74,10 +78,7 @@ public class NetworkUtility {
public static String urlForSocketAddress(final String scheme, final InetSocketAddress address) {
String hostName = address.getHostName();
if ("0.0.0.0".equals(hostName)) {
hostName = InetAddress.getLoopbackAddress().getHostName();
}
if ("0:0:0:0:0:0:0:0".equals(hostName)) {
if (isUnspecifiedAddress(hostName)) {
hostName = InetAddress.getLoopbackAddress().getHostName();
}
if (hostName.contains(":")) {
@ -85,4 +86,16 @@ public class NetworkUtility {
}
return scheme + "://" + hostName + ":" + address.getPort();
}
public static boolean isNetworkInterfaceAvailable(final String ipAddress)
throws SocketException, UnknownHostException {
if (isUnspecifiedAddress(ipAddress)) {
return true;
}
return NetworkInterface.getByInetAddress(InetAddress.getByName(ipAddress)) != null;
}
public static boolean isUnspecifiedAddress(final String ipAddress) {
return INADDR_ANY.equals(ipAddress) || INADDR6_ANY.equals(ipAddress);
}
}

Loading…
Cancel
Save