diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/BesuNode.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/BesuNode.java index 5f30941f232..2cf7410669b 100644 --- a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/BesuNode.java +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/BesuNode.java @@ -31,6 +31,7 @@ import org.hyperledger.besu.ethereum.core.Util; import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration; import org.hyperledger.besu.ethereum.p2p.config.NetworkingConfiguration; +import org.hyperledger.besu.ethereum.p2p.config.P2PConfiguration; import org.hyperledger.besu.ethereum.p2p.rlpx.connections.netty.TLSConfiguration; import org.hyperledger.besu.ethereum.permissioning.PermissioningConfiguration; import org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration; @@ -94,8 +95,8 @@ public class BesuNode implements NodeConfiguration, RunnableNode, AutoCloseable private final Path homeDirectory; private KeyPair keyPair; private final Properties portsProperties = new Properties(); - private final Boolean p2pEnabled; - private final int p2pPort; + + private final P2PConfiguration p2PConfiguration; private final Optional tlsConfiguration; private final NetworkingConfiguration networkingConfiguration; private final boolean revertReasonEnabled; @@ -116,7 +117,6 @@ public class BesuNode implements NodeConfiguration, RunnableNode, AutoCloseable private final GenesisConfigurationProvider genesisConfigProvider; private final boolean devMode; private final NetworkName network; - private final boolean discoveryEnabled; private final List bootnodes = new ArrayList<>(); private final boolean bootnodeEligible; private final boolean secp256k1Native; @@ -137,6 +137,7 @@ public class BesuNode implements NodeConfiguration, RunnableNode, AutoCloseable public BesuNode( final String name, final Optional dataPath, + final P2PConfiguration p2PConfiguration, final MiningParameters miningParameters, final TransactionPoolConfiguration txPoolConfiguration, final JsonRpcConfiguration jsonRpcConfiguration, @@ -151,11 +152,8 @@ public BesuNode( final boolean devMode, final NetworkName network, final GenesisConfigurationProvider genesisConfigProvider, - final boolean p2pEnabled, - final int p2pPort, final Optional tlsConfiguration, final NetworkingConfiguration networkingConfiguration, - final boolean discoveryEnabled, final boolean bootnodeEligible, final boolean revertReasonEnabled, final boolean secp256k1Native, @@ -200,11 +198,9 @@ public BesuNode( this.genesisConfigProvider = genesisConfigProvider; this.devMode = devMode; this.network = network; - this.p2pEnabled = p2pEnabled; - this.p2pPort = p2pPort; + this.p2PConfiguration = p2PConfiguration; this.tlsConfiguration = tlsConfiguration; this.networkingConfiguration = networkingConfiguration; - this.discoveryEnabled = discoveryEnabled; this.bootnodeEligible = bootnodeEligible; this.revertReasonEnabled = revertReasonEnabled; this.secp256k1Native = secp256k1Native; @@ -290,7 +286,7 @@ public URI enodeUrl() { } public String getP2pPort() { - return String.valueOf(p2pPort); + return String.valueOf(p2PConfiguration.getPort()); } private String getRuntimeP2pPort() { @@ -301,6 +297,10 @@ private String getRuntimeP2pPort() { return port; } + public P2PConfiguration getP2PConfiguration() { + return p2PConfiguration; + } + private String getDiscoveryPort() { final String port = portsProperties.getProperty("discovery"); if (port == null) { @@ -647,7 +647,7 @@ public List getBootnodes() { @Override public boolean isP2pEnabled() { - return p2pEnabled; + return p2PConfiguration.isP2pEnabled(); } public Optional getTLSConfiguration() { @@ -716,7 +716,7 @@ public boolean isAltbn128Native() { @Override public boolean isDiscoveryEnabled() { - return discoveryEnabled; + return p2PConfiguration.isDiscoveryEnabled(); } Optional getPermissioningConfiguration() { @@ -769,8 +769,8 @@ public String toString() { .add("name", name) .add("homeDirectory", homeDirectory) .add("keyPair", keyPair) - .add("p2pEnabled", p2pEnabled) - .add("discoveryEnabled", discoveryEnabled) + .add("p2pEnabled", p2PConfiguration.isP2pEnabled()) + .add("discoveryEnabled", p2PConfiguration.isDiscoveryEnabled()) .add("privacyEnabled", privacyParameters.isEnabled()) .toString(); } diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ThreadBesuNodeRunner.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ThreadBesuNodeRunner.java index 0448d324e64..58f7f09e4ee 100644 --- a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ThreadBesuNodeRunner.java +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ThreadBesuNodeRunner.java @@ -35,6 +35,7 @@ import org.hyperledger.besu.ethereum.eth.sync.SynchronizerConfiguration; import org.hyperledger.besu.ethereum.eth.transactions.ImmutableTransactionPoolConfiguration; import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration; +import org.hyperledger.besu.ethereum.p2p.config.P2PConfiguration; import org.hyperledger.besu.ethereum.p2p.peers.EnodeURLImpl; import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueStorageProvider; import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueStorageProviderBuilder; @@ -236,8 +237,6 @@ public void startNode(final BesuNode node) { .transactionPoolValidatorService(transactionPoolValidatorServiceImpl) .build(); - final int maxPeers = 25; - builder .synchronizerConfiguration(new SynchronizerConfiguration.Builder().build()) .dataDirectory(node.homeDirectory()) @@ -253,10 +252,8 @@ public void startNode(final BesuNode node) { .storageProvider(storageProvider) .gasLimitCalculator(GasLimitCalculator.constant()) .evmConfiguration(EvmConfiguration.DEFAULT) - .maxPeers(maxPeers) - .maxRemotelyInitiatedPeers(15) .networkConfiguration(node.getNetworkingConfiguration()) - .randomPeerPriority(false); + .p2PConfiguration(P2PConfiguration.createDefault()); node.getGenesisConfig() .map(GenesisConfigFile::fromConfig) @@ -276,9 +273,7 @@ public void startNode(final BesuNode node) { .vertx(Vertx.vertx()) .besuController(besuController) .ethNetworkConfig(ethNetworkConfig) - .discovery(node.isDiscoveryEnabled()) - .p2pAdvertisedHost(node.getHostName()) - .p2pListenPort(0) + .p2PConfiguration(node.getP2PConfiguration()) .networkingConfiguration(node.getNetworkingConfiguration()) .jsonRpcConfiguration(node.jsonRpcConfiguration()) .webSocketConfiguration(node.webSocketConfiguration()) @@ -287,7 +282,6 @@ public void startNode(final BesuNode node) { .metricsSystem(metricsSystem) .permissioningService(permissioningService) .metricsConfiguration(node.getMetricsConfiguration()) - .p2pEnabled(node.isP2pEnabled()) .p2pTLSConfiguration(node.getTLSConfiguration()) .graphQLConfiguration(GraphQLConfiguration.createDefault()) .staticNodes( diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeConfiguration.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeConfiguration.java index 7bffe9d7750..05cc81764b4 100644 --- a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeConfiguration.java +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeConfiguration.java @@ -24,6 +24,7 @@ import org.hyperledger.besu.ethereum.core.PrivacyParameters; import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration; import org.hyperledger.besu.ethereum.p2p.config.NetworkingConfiguration; +import org.hyperledger.besu.ethereum.p2p.config.P2PConfiguration; import org.hyperledger.besu.ethereum.p2p.rlpx.connections.netty.TLSConfiguration; import org.hyperledger.besu.ethereum.permissioning.PermissioningConfiguration; import org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration; @@ -52,11 +53,9 @@ public class BesuNodeConfiguration { private final Optional keyFilePath; private final boolean devMode; private final GenesisConfigurationProvider genesisConfigProvider; - private final boolean p2pEnabled; - private final int p2pPort; + private final P2PConfiguration p2pConfiguration; private final Optional tlsConfiguration; private final NetworkingConfiguration networkingConfiguration; - private final boolean discoveryEnabled; private final boolean bootnodeEligible; private final boolean revertReasonEnabled; private final boolean secp256k1Native; @@ -89,11 +88,9 @@ public class BesuNodeConfiguration { final boolean devMode, final NetworkName network, final GenesisConfigurationProvider genesisConfigProvider, - final boolean p2pEnabled, - final int p2pPort, + final P2PConfiguration p2pConfiguration, final Optional tlsConfiguration, final NetworkingConfiguration networkingConfiguration, - final boolean discoveryEnabled, final boolean bootnodeEligible, final boolean revertReasonEnabled, final boolean secp256k1Native, @@ -123,11 +120,9 @@ public class BesuNodeConfiguration { this.devMode = devMode; this.network = network; this.genesisConfigProvider = genesisConfigProvider; - this.p2pEnabled = p2pEnabled; - this.p2pPort = p2pPort; + this.p2pConfiguration = p2pConfiguration; this.tlsConfiguration = tlsConfiguration; this.networkingConfiguration = networkingConfiguration; - this.discoveryEnabled = discoveryEnabled; this.bootnodeEligible = bootnodeEligible; this.revertReasonEnabled = revertReasonEnabled; this.secp256k1Native = secp256k1Native; @@ -199,20 +194,12 @@ public boolean isDevMode() { return devMode; } - public boolean isDiscoveryEnabled() { - return discoveryEnabled; - } - public GenesisConfigurationProvider getGenesisConfigProvider() { return genesisConfigProvider; } - public boolean isP2pEnabled() { - return p2pEnabled; - } - - public int getP2pPort() { - return p2pPort; + public P2PConfiguration getP2PConfiguration() { + return p2pConfiguration; } public Optional getTLSConfiguration() { diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeConfigurationBuilder.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeConfigurationBuilder.java index 86fb8ab5caf..5175cd8ed21 100644 --- a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeConfigurationBuilder.java +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeConfigurationBuilder.java @@ -37,6 +37,7 @@ import org.hyperledger.besu.ethereum.core.PrivacyParameters; import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration; import org.hyperledger.besu.ethereum.p2p.config.NetworkingConfiguration; +import org.hyperledger.besu.ethereum.p2p.config.P2PConfiguration; import org.hyperledger.besu.ethereum.p2p.rlpx.connections.netty.TLSConfiguration; import org.hyperledger.besu.ethereum.permissioning.PermissioningConfiguration; import org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration; @@ -66,6 +67,7 @@ public class BesuNodeConfigurationBuilder { .build(); private TransactionPoolConfiguration transactionPoolConfiguration = TransactionPoolConfiguration.DEFAULT; + private P2PConfiguration p2pConfiguration = new P2PConfiguration.Builder().build(); private JsonRpcConfiguration jsonRpcConfiguration = JsonRpcConfiguration.createDefault(); private JsonRpcConfiguration engineRpcConfiguration = JsonRpcConfiguration.createEngineDefault(); private WebSocketConfiguration webSocketConfiguration = WebSocketConfiguration.createDefault(); @@ -78,11 +80,8 @@ public class BesuNodeConfigurationBuilder { private String keyFilePath = null; private boolean devMode = true; private GenesisConfigurationProvider genesisConfigProvider = ignore -> Optional.empty(); - private Boolean p2pEnabled = true; - private int p2pPort = 0; private Optional tlsConfiguration = Optional.empty(); private final NetworkingConfiguration networkingConfiguration = NetworkingConfiguration.create(); - private boolean discoveryEnabled = true; private boolean bootnodeEligible = true; private boolean revertReasonEnabled = false; private NetworkName network = null; @@ -246,6 +245,11 @@ public BesuNodeConfigurationBuilder jsonRpcAuthenticationUsingECDSA() throws URI return this; } + public BesuNodeConfigurationBuilder p2PConfiguration(final P2PConfiguration p2pConfiguration) { + this.p2pConfiguration = p2pConfiguration; + return this; + } + public BesuNodeConfigurationBuilder webSocketConfiguration( final WebSocketConfiguration webSocketConfiguration) { this.webSocketConfiguration = webSocketConfiguration; @@ -359,16 +363,6 @@ public BesuNodeConfigurationBuilder genesisConfigProvider( return this; } - public BesuNodeConfigurationBuilder p2pEnabled(final Boolean p2pEnabled) { - this.p2pEnabled = p2pEnabled; - return this; - } - - public BesuNodeConfigurationBuilder p2pPort(final int p2pPort) { - this.p2pPort = p2pPort; - return this; - } - private static Path toPath(final String path) throws Exception { return Path.of(BesuNodeConfigurationBuilder.class.getResource(path).toURI()); } @@ -427,11 +421,6 @@ public BesuNodeConfigurationBuilder p2pTLSEnabled(final String name, final Strin return this; } - public BesuNodeConfigurationBuilder discoveryEnabled(final boolean discoveryEnabled) { - this.discoveryEnabled = discoveryEnabled; - return this; - } - public BesuNodeConfigurationBuilder plugins(final List plugins) { this.plugins.clear(); this.plugins.addAll(plugins); @@ -524,11 +513,9 @@ public BesuNodeConfiguration build() { devMode, network, genesisConfigProvider, - p2pEnabled, - p2pPort, + p2pConfiguration, tlsConfiguration, networkingConfiguration, - discoveryEnabled, bootnodeEligible, revertReasonEnabled, secp256K1Native, diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeFactory.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeFactory.java index 1ea29388bd5..b64a2e8a15d 100644 --- a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeFactory.java +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeFactory.java @@ -28,6 +28,7 @@ import org.hyperledger.besu.ethereum.core.InMemoryPrivacyStorageProvider; import org.hyperledger.besu.ethereum.core.MiningParameters; import org.hyperledger.besu.ethereum.core.PrivacyParameters; +import org.hyperledger.besu.ethereum.p2p.config.P2PConfiguration; import org.hyperledger.besu.ethereum.permissioning.LocalPermissioningConfiguration; import org.hyperledger.besu.ethereum.permissioning.PermissioningConfiguration; import org.hyperledger.besu.pki.keystore.KeyStoreWrapper; @@ -54,10 +55,16 @@ public class BesuNodeFactory { private final NodeConfigurationFactory node = new NodeConfigurationFactory(); + private final P2PConfiguration P2P_DISABLED = + P2PConfiguration.builder().port(0).p2pEnabled(false).build(); + private final P2PConfiguration DISCOVERY_DISABLED = + P2PConfiguration.builder().port(0).discoveryEnabled(false).build(); + public BesuNode create(final BesuNodeConfiguration config) throws IOException { return new BesuNode( config.getName(), config.getDataPath(), + config.getP2PConfiguration(), config.getMiningParameters(), config.getTransactionPoolConfiguration(), config.getJsonRpcConfiguration(), @@ -72,11 +79,8 @@ public BesuNode create(final BesuNodeConfiguration config) throws IOException { config.isDevMode(), config.getNetwork(), config.getGenesisConfigProvider(), - config.isP2pEnabled(), - config.getP2pPort(), config.getTLSConfiguration(), config.getNetworkingConfiguration(), - config.isDiscoveryEnabled(), config.isBootnodeEligible(), config.isRevertReasonEnabled(), config.isSecp256k1Native(), @@ -183,7 +187,7 @@ public BesuNode createArchiveNodeWithDiscoveryDisabledAndAdmin(final String name .name(name) .jsonRpcConfiguration(node.jsonRpcConfigWithAdmin()) .webSocketEnabled() - .discoveryEnabled(false) + .p2PConfiguration(DISCOVERY_DISABLED) .build()); } @@ -198,7 +202,6 @@ public BesuNode createArchiveNodeNetServicesEnabled(final String name) throws IO // .setMetricsConfiguration(metricsConfiguration) .jsonRpcConfiguration(node.jsonRpcConfigWithAdmin()) .webSocketEnabled() - .p2pEnabled(true) .build()); } @@ -207,7 +210,7 @@ public BesuNode createArchiveNodeNetServicesDisabled(final String name) throws I new BesuNodeConfigurationBuilder() .name(name) .jsonRpcConfiguration(node.jsonRpcConfigWithAdmin()) - .p2pEnabled(false) + .p2PConfiguration(P2P_DISABLED) .build()); } @@ -277,7 +280,7 @@ public BesuNode createNodeWithP2pDisabled(final String name) throws IOException return create( new BesuNodeConfigurationBuilder() .name(name) - .p2pEnabled(false) + .p2PConfiguration(P2P_DISABLED) .jsonRpcConfiguration(node.createJsonRpcEnabledConfig()) .build()); } @@ -361,7 +364,7 @@ public BesuNode createNodeWithNoDiscovery(final String name) throws IOException return create( new BesuNodeConfigurationBuilder() .name(name) - .discoveryEnabled(false) + .p2PConfiguration(DISCOVERY_DISABLED) .engineRpcEnabled(false) .build()); } @@ -480,11 +483,12 @@ public BesuNode createIbft2Node(final String name, final boolean fixedPort) thro .devMode(false) .genesisConfigProvider(GenesisConfigurationFactory::createIbft2GenesisConfig); if (fixedPort) { - builder.p2pPort( + int port = Math.abs(name.hashCode() % 60000) + 1024 - + 500); // Generate a consistent port for p2p based on node name (+ 500 to avoid + + 500; // Generate a consistent port for p2p based on node name (+ 500 to avoid // clashing with RPC port or other nodes with a similar name) + builder.p2PConfiguration(P2PConfiguration.builder().port(port).build()); } return create(builder.build()); } @@ -532,11 +536,12 @@ public BesuNode createQbftNode(final String name, final boolean fixedPort) throw .devMode(false) .genesisConfigProvider(GenesisConfigurationFactory::createQbftGenesisConfig); if (fixedPort) { - builder.p2pPort( + int port = Math.abs(name.hashCode() % 60000) + 1024 - + 500); // Generate a consistent port for p2p based on node name (+ 500 to avoid + + 500; // Generate a consistent port for p2p based on node name (+ 500 to avoid // clashing with RPC port or other nodes with a similar name) + builder.p2PConfiguration(P2PConfiguration.builder().port(port).build()); } return create(builder.build()); } @@ -719,7 +724,7 @@ private BesuNodeConfigurationBuilder createConfigurationBuilderWithStaticNodes( .name(name) .jsonRpcEnabled() .webSocketEnabled() - .discoveryEnabled(false) + .p2PConfiguration(DISCOVERY_DISABLED) .staticNodes(staticNodesUrls) .bootnodeEligible(false); } diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/privacy/PrivacyNode.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/privacy/PrivacyNode.java index 32a5a0fbf4a..4c39e6ecfe5 100644 --- a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/privacy/PrivacyNode.java +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/privacy/PrivacyNode.java @@ -100,6 +100,7 @@ public PrivacyNode( new BesuNode( besuConfig.getName(), besuConfig.getDataPath(), + besuConfig.getP2PConfiguration(), besuConfig.getMiningParameters(), besuConfig.getTransactionPoolConfiguration(), besuConfig.getJsonRpcConfiguration(), @@ -114,11 +115,8 @@ public PrivacyNode( besuConfig.isDevMode(), besuConfig.getNetwork(), besuConfig.getGenesisConfigProvider(), - besuConfig.isP2pEnabled(), - besuConfig.getP2pPort(), besuConfig.getTLSConfiguration(), besuConfig.getNetworkingConfiguration(), - besuConfig.isDiscoveryEnabled(), besuConfig.isBootnodeEligible(), besuConfig.isRevertReasonEnabled(), besuConfig.isSecp256k1Native(), diff --git a/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/config/BootNodesGenesisSetupTest.java b/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/config/BootNodesGenesisSetupTest.java index 616afc19669..ce3046f9c9e 100644 --- a/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/config/BootNodesGenesisSetupTest.java +++ b/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/config/BootNodesGenesisSetupTest.java @@ -18,6 +18,7 @@ import org.hyperledger.besu.crypto.SECPPrivateKey; import org.hyperledger.besu.crypto.SECPPublicKey; import org.hyperledger.besu.crypto.SignatureAlgorithm; +import org.hyperledger.besu.ethereum.p2p.config.P2PConfiguration; import org.hyperledger.besu.tests.acceptance.dsl.AcceptanceTestBase; import org.hyperledger.besu.tests.acceptance.dsl.node.Node; import org.hyperledger.besu.tests.acceptance.dsl.node.configuration.BesuNodeConfigurationBuilder; @@ -108,7 +109,7 @@ private BesuNodeConfigurationBuilder configureNode( return nodeBuilder .devMode(false) .keyPair(keyPair) - .p2pPort(p2pBindingPort) + .p2PConfiguration(P2PConfiguration.builder().port(p2pBindingPort).build()) .genesisConfigProvider( (nodes) -> Optional.of( diff --git a/besu/src/main/java/org/hyperledger/besu/RunnerBuilder.java b/besu/src/main/java/org/hyperledger/besu/RunnerBuilder.java index 7ed627cfc17..09910914b3c 100644 --- a/besu/src/main/java/org/hyperledger/besu/RunnerBuilder.java +++ b/besu/src/main/java/org/hyperledger/besu/RunnerBuilder.java @@ -78,6 +78,7 @@ import org.hyperledger.besu.ethereum.mainnet.precompiles.privacy.FlexiblePrivacyPrecompiledContract; import org.hyperledger.besu.ethereum.p2p.config.DiscoveryConfiguration; import org.hyperledger.besu.ethereum.p2p.config.NetworkingConfiguration; +import org.hyperledger.besu.ethereum.p2p.config.P2PConfiguration; import org.hyperledger.besu.ethereum.p2p.config.RlpxConfiguration; import org.hyperledger.besu.ethereum.p2p.config.SubProtocolConfiguration; import org.hyperledger.besu.ethereum.p2p.network.DefaultP2PNetwork; @@ -125,7 +126,6 @@ import org.hyperledger.besu.services.BesuPluginContextImpl; import org.hyperledger.besu.services.PermissioningServiceImpl; import org.hyperledger.besu.services.RpcEndpointServiceImpl; -import org.hyperledger.besu.util.NetworkUtility; import java.io.IOException; import java.nio.file.Path; @@ -147,7 +147,6 @@ import graphql.GraphQL; import io.vertx.core.Vertx; import io.vertx.core.VertxOptions; -import org.apache.commons.net.util.SubnetUtils.SubnetInfo; import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.units.bigints.UInt256; import org.slf4j.Logger; @@ -162,13 +161,8 @@ public class RunnerBuilder { private BesuController besuController; private NetworkingConfiguration networkingConfiguration = NetworkingConfiguration.create(); - private final Collection bannedNodeIds = new ArrayList<>(); - private boolean p2pEnabled = true; + private P2PConfiguration p2PConfiguration; private Optional p2pTLSConfiguration = Optional.empty(); - private boolean discovery; - private String p2pAdvertisedHost; - private String p2pListenInterface = NetworkUtility.INADDR_ANY; - private int p2pListenPort; private NatMethod natMethod = NatMethod.AUTO; private String natManagerServiceName; private boolean natMethodFallbackEnabled; @@ -194,7 +188,6 @@ public class RunnerBuilder { private JsonRpcIpcConfiguration jsonRpcIpcConfiguration; private boolean legacyForkIdEnabled; private Optional enodeDnsConfiguration; - private List allowedSubnets = new ArrayList<>(); /** Instantiates a new Runner builder. */ public RunnerBuilder() {} @@ -222,13 +215,16 @@ public RunnerBuilder besuController(final BesuController besuController) { } /** - * P2p enabled. + * P2P Configuration. * - * @param p2pEnabled the p 2 p enabled + * @param p2PConfiguration the p2pConfiguration * @return the runner builder */ - public RunnerBuilder p2pEnabled(final boolean p2pEnabled) { - this.p2pEnabled = p2pEnabled; + public RunnerBuilder p2PConfiguration(final P2PConfiguration p2PConfiguration) { + checkArgument( + !isNull(p2PConfiguration.getP2pInterface()), + "Invalid null value supplied for p2pListenInterface"); + this.p2PConfiguration = p2PConfiguration; return this; } @@ -256,17 +252,6 @@ public RunnerBuilder p2pTLSConfiguration(final Optional p2pTLS return this; } - /** - * Enable Discovery. - * - * @param discovery the discovery - * @return the runner builder - */ - public RunnerBuilder discovery(final boolean discovery) { - this.discovery = discovery; - return this; - } - /** * Add Eth network config. * @@ -290,40 +275,6 @@ public RunnerBuilder networkingConfiguration( return this; } - /** - * Add P2p advertised host. - * - * @param p2pAdvertisedHost the P2P advertised host - * @return the runner builder - */ - public RunnerBuilder p2pAdvertisedHost(final String p2pAdvertisedHost) { - this.p2pAdvertisedHost = p2pAdvertisedHost; - return this; - } - - /** - * Add P2P Listener interface ip/host name. - * - * @param ip the ip - * @return the runner builder - */ - public RunnerBuilder p2pListenInterface(final String ip) { - checkArgument(!isNull(ip), "Invalid null value supplied for p2pListenInterface"); - this.p2pListenInterface = ip; - return this; - } - - /** - * Add P2P listen port. - * - * @param p2pListenPort the p 2 p listen port - * @return the runner builder - */ - public RunnerBuilder p2pListenPort(final int p2pListenPort) { - this.p2pListenPort = p2pListenPort; - return this; - } - /** * Add Nat method. * @@ -458,17 +409,6 @@ public RunnerBuilder dataDir(final Path dataDir) { return this; } - /** - * Add list of Banned node id. - * - * @param bannedNodeIds the banned node ids - * @return the runner builder - */ - public RunnerBuilder bannedNodeIds(final Collection bannedNodeIds) { - this.bannedNodeIds.addAll(bannedNodeIds); - return this; - } - /** * Add Metrics configuration. * @@ -592,17 +532,6 @@ public RunnerBuilder enodeDnsConfiguration(final EnodeDnsConfiguration enodeDnsC return this; } - /** - * Add subnet configuration - * - * @param allowedSubnets the allowedSubnets - * @return the runner builder - */ - public RunnerBuilder allowedSubnets(final List allowedSubnets) { - this.allowedSubnets = allowedSubnets; - return this; - } - /** * Build Runner instance. * @@ -614,10 +543,10 @@ public Runner build() { final DiscoveryConfiguration discoveryConfiguration = DiscoveryConfiguration.create() - .setBindHost(p2pListenInterface) - .setBindPort(p2pListenPort) - .setAdvertisedHost(p2pAdvertisedHost); - if (discovery) { + .setBindHost(p2PConfiguration.getP2pInterface()) + .setBindPort(p2PConfiguration.getPort()) + .setAdvertisedHost(p2PConfiguration.getHost()); + if (p2PConfiguration.isDiscoveryEnabled()) { final List bootstrap; if (ethNetworkConfig.bootNodes() == null) { bootstrap = EthNetworkConfig.getNetworkConfig(NetworkName.MAINNET).bootNodes(); @@ -653,16 +582,17 @@ public Runner build() { final RlpxConfiguration rlpxConfiguration = RlpxConfiguration.create() - .setBindHost(p2pListenInterface) - .setBindPort(p2pListenPort) + .setBindHost(p2PConfiguration.getP2pInterface()) + .setBindPort(p2PConfiguration.getPort()) .setSupportedProtocols(subProtocols) .setClientId(BesuInfo.nodeName(identityString)); networkingConfiguration.setRlpx(rlpxConfiguration).setDiscovery(discoveryConfiguration); final PeerPermissionsDenylist bannedNodes = PeerPermissionsDenylist.create(); - bannedNodeIds.forEach(bannedNodes::add); + p2PConfiguration.getBannedNodeIds().forEach(bannedNodes::add); - PeerPermissionSubnet peerPermissionSubnet = new PeerPermissionSubnet(allowedSubnets); + PeerPermissionSubnet peerPermissionSubnet = + new PeerPermissionSubnet(p2PConfiguration.getAllowedSubnets()); final PeerPermissions defaultPeerPermissions = PeerPermissions.combine(peerPermissionSubnet, bannedNodes); @@ -720,7 +650,7 @@ public Runner build() { NetworkRunner.builder() .protocolManagers(protocolManagers) .subProtocols(subProtocols) - .network(p2pEnabled ? activeNetwork : inactiveNetwork) + .network(p2PConfiguration.isP2pEnabled() ? activeNetwork : inactiveNetwork) .metricsSystem(metricsSystem) .ethPeersShouldConnect(ethPeers::shouldTryToConnect) .build(); @@ -1188,7 +1118,10 @@ private Optional buildNatManager(final NatMethod natMethod) { return Optional.of(new UpnpNatManager()); case DOCKER: return Optional.of( - new DockerNatManager(p2pAdvertisedHost, p2pListenPort, jsonRpcConfiguration.getPort())); + new DockerNatManager( + p2PConfiguration.getHost(), + p2PConfiguration.getPort(), + jsonRpcConfiguration.getPort())); case KUBERNETES: return Optional.of(new KubernetesNatManager(natManagerServiceName)); case NONE: diff --git a/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java b/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java index 198c26fe2a8..46f66fb29e9 100644 --- a/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java +++ b/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java @@ -15,7 +15,6 @@ package org.hyperledger.besu.cli; import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.base.Preconditions.checkState; import static java.util.Arrays.asList; import static java.util.Collections.singletonList; import static org.hyperledger.besu.cli.DefaultCommandValues.getDefaultBesuDataPath; @@ -25,6 +24,7 @@ import static org.hyperledger.besu.controller.BesuController.DATABASE_PATH; import static org.hyperledger.besu.ethereum.api.jsonrpc.JsonRpcConfiguration.DEFAULT_ENGINE_JSON_RPC_PORT; import static org.hyperledger.besu.ethereum.api.jsonrpc.authentication.EngineAuthService.EPHEMERAL_JWT_FILE; +import static org.hyperledger.besu.ethereum.p2p.config.AutoDiscoverDefaultIP.autoDiscoverDefaultIP; import static org.hyperledger.besu.metrics.BesuMetricCategory.DEFAULT_METRIC_CATEGORIES; import static org.hyperledger.besu.metrics.MetricsProtocol.PROMETHEUS; import static org.hyperledger.besu.metrics.prometheus.MetricsConfiguration.DEFAULT_METRICS_PORT; @@ -41,8 +41,6 @@ import org.hyperledger.besu.cli.config.NetworkName; import org.hyperledger.besu.cli.config.ProfilesCompletionCandidates; import org.hyperledger.besu.cli.converter.MetricCategoryConverter; -import org.hyperledger.besu.cli.converter.PercentageConverter; -import org.hyperledger.besu.cli.converter.SubnetInfoConverter; import org.hyperledger.besu.cli.custom.JsonRPCAllowlistHostsProperty; import org.hyperledger.besu.cli.error.BesuExecutionExceptionHandler; import org.hyperledger.besu.cli.error.BesuParameterExceptionHandler; @@ -55,6 +53,7 @@ import org.hyperledger.besu.cli.options.stable.JsonRpcHttpOptions; import org.hyperledger.besu.cli.options.stable.LoggingLevelOption; import org.hyperledger.besu.cli.options.stable.NodePrivateKeyFileOption; +import org.hyperledger.besu.cli.options.stable.P2POptions; import org.hyperledger.besu.cli.options.stable.P2PTLSConfigOptions; import org.hyperledger.besu.cli.options.stable.PermissionsOptions; import org.hyperledger.besu.cli.options.stable.PluginsConfigurationOptions; @@ -125,6 +124,7 @@ import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration; import org.hyperledger.besu.ethereum.mainnet.FrontierTargetingGasLimitCalculator; import org.hyperledger.besu.ethereum.p2p.config.DiscoveryConfiguration; +import org.hyperledger.besu.ethereum.p2p.config.P2PConfiguration; import org.hyperledger.besu.ethereum.p2p.peers.EnodeDnsConfiguration; import org.hyperledger.besu.ethereum.p2p.peers.EnodeURLImpl; import org.hyperledger.besu.ethereum.p2p.peers.StaticNodesParser; @@ -200,7 +200,6 @@ import org.hyperledger.besu.util.LogConfigurator; import org.hyperledger.besu.util.NetworkUtility; import org.hyperledger.besu.util.PermissioningConfigurationValidator; -import org.hyperledger.besu.util.number.Fraction; import org.hyperledger.besu.util.number.Percentage; import org.hyperledger.besu.util.number.PositiveNumber; @@ -208,11 +207,8 @@ 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.URL; -import java.net.UnknownHostException; import java.nio.file.Path; import java.time.Clock; import java.util.ArrayList; @@ -240,7 +236,6 @@ import io.vertx.core.VertxOptions; import io.vertx.core.json.DecodeException; import io.vertx.core.metrics.MetricsOptions; -import org.apache.commons.net.util.SubnetUtils.SubnetInfo; import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.units.bigints.UInt256; import org.slf4j.Logger; @@ -337,9 +332,6 @@ public class BesuCommand implements DefaultCommandValues, Runnable { private RocksDBPlugin rocksDBPlugin; - private int maxPeers; - private int maxRemoteInitiatedPeers; - // CLI options defined by user at runtime. // Options parsing is done with CLI library Picocli https://picocli.info/ @@ -384,157 +376,13 @@ public class BesuCommand implements DefaultCommandValues, Runnable { // P2P Discovery Option Group @CommandLine.ArgGroup(validate = false, heading = "@|bold P2P Discovery Options|@%n") - P2PDiscoveryOptionGroup p2PDiscoveryOptionGroup = new P2PDiscoveryOptionGroup(); + P2POptions p2POptions = new P2POptions(); private final TransactionSelectionServiceImpl transactionSelectionServiceImpl; private final TransactionPoolValidatorServiceImpl transactionValidatorServiceImpl; private final TransactionSimulationServiceImpl transactionSimulationServiceImpl; private final BlockchainServiceImpl blockchainServiceImpl; - static class P2PDiscoveryOptionGroup { - - // Public IP stored to prevent having to research it each time we need it. - private InetAddress autoDiscoveredDefaultIP = null; - - // Completely disables P2P within Besu. - @Option( - names = {"--p2p-enabled"}, - description = "Enable P2P functionality (default: ${DEFAULT-VALUE})", - arity = "1") - private final Boolean p2pEnabled = true; - - // Boolean option to indicate if peers should NOT be discovered, default to - // false indicates that - // the peers should be discovered by default. - // - // This negative option is required because of the nature of the option that is - // true when - // added on the command line. You can't do --option=false, so false is set as - // default - // and you have not to set the option at all if you want it false. - // This seems to be the only way it works with Picocli. - // Also many other software use the same negative option scheme for false - // defaults - // meaning that it's probably the right way to handle disabling options. - @Option( - names = {"--discovery-enabled"}, - description = "Enable P2P discovery (default: ${DEFAULT-VALUE})", - arity = "1") - private final Boolean peerDiscoveryEnabled = true; - - // A list of bootstrap nodes can be passed - // and a hardcoded list will be used otherwise by the Runner. - // NOTE: we have no control over default value here. - @Option( - names = {"--bootnodes"}, - paramLabel = "", - description = - "Comma separated enode URLs for P2P discovery bootstrap. " - + "Default is a predefined list.", - split = ",", - arity = "0..*") - private final List bootNodes = null; - - @SuppressWarnings({"FieldCanBeFinal", "FieldMayBeFinal"}) // PicoCLI requires non-final Strings. - @Option( - names = {"--p2p-host"}, - paramLabel = MANDATORY_HOST_FORMAT_HELP, - description = "IP address this node advertises to its peers (default: ${DEFAULT-VALUE})", - arity = "1") - private String p2pHost = autoDiscoverDefaultIP().getHostAddress(); - - @SuppressWarnings({"FieldCanBeFinal", "FieldMayBeFinal"}) // PicoCLI requires non-final Strings. - @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, - description = "Port on which to listen for P2P communication (default: ${DEFAULT-VALUE})", - arity = "1") - private final Integer p2pPort = EnodeURLImpl.DEFAULT_LISTENING_PORT; - - @Option( - names = {"--max-peers", "--p2p-peer-upper-bound"}, - paramLabel = MANDATORY_INTEGER_FORMAT_HELP, - description = "Maximum P2P connections that can be established (default: ${DEFAULT-VALUE})") - private final Integer maxPeers = DEFAULT_MAX_PEERS; - - @Option( - names = {"--remote-connections-limit-enabled"}, - description = - "Whether to limit the number of P2P connections initiated remotely. (default: ${DEFAULT-VALUE})") - private final Boolean isLimitRemoteWireConnectionsEnabled = true; - - @Option( - names = {"--remote-connections-max-percentage"}, - paramLabel = MANDATORY_DOUBLE_FORMAT_HELP, - description = - "The maximum percentage of P2P connections that can be initiated remotely. Must be between 0 and 100 inclusive. (default: ${DEFAULT-VALUE})", - arity = "1", - converter = PercentageConverter.class) - private final Percentage maxRemoteConnectionsPercentage = - Fraction.fromFloat(DEFAULT_FRACTION_REMOTE_WIRE_CONNECTIONS_ALLOWED).toPercentage(); - - @SuppressWarnings({"FieldCanBeFinal", "FieldMayBeFinal"}) // PicoCLI requires non-final Strings. - @CommandLine.Option( - names = {"--discovery-dns-url"}, - description = "Specifies the URL to use for DNS discovery") - private String discoveryDnsUrl = null; - - @Option( - names = {"--random-peer-priority-enabled"}, - description = - "Allow for incoming connections to be prioritized randomly. This will prevent (typically small, stable) networks from forming impenetrable peer cliques. (default: ${DEFAULT-VALUE})") - private final Boolean randomPeerPriority = Boolean.FALSE; - - @Option( - names = {"--banned-node-ids", "--banned-node-id"}, - paramLabel = MANDATORY_NODE_ID_FORMAT_HELP, - description = "A list of node IDs to ban from the P2P network.", - split = ",", - arity = "1..*") - void setBannedNodeIds(final List values) { - try { - bannedNodeIds = - values.stream() - .filter(value -> !value.isEmpty()) - .map(EnodeURLImpl::parseNodeId) - .collect(Collectors.toList()); - } catch (final IllegalArgumentException e) { - throw new ParameterException( - new CommandLine(this), - "Invalid ids supplied to '--banned-node-ids'. " + e.getMessage()); - } - } - - private Collection bannedNodeIds = new ArrayList<>(); - - // Used to discover the default IP of the client. - // Loopback IP is used by default as this is how smokeTests require it to be - // and it's probably a good security behaviour to default only on the localhost. - private InetAddress autoDiscoverDefaultIP() { - autoDiscoveredDefaultIP = - Optional.ofNullable(autoDiscoveredDefaultIP).orElseGet(InetAddress::getLoopbackAddress); - - return autoDiscoveredDefaultIP; - } - - @Option( - names = {"--net-restrict"}, - arity = "1..*", - split = ",", - converter = SubnetInfoConverter.class, - description = - "Comma-separated list of allowed IP subnets (e.g., '192.168.1.0/24,10.0.0.0/8').") - private List allowedSubnets; - } - @Option( names = {"--sync-mode"}, paramLabel = MANDATORY_MODE_FORMAT_HELP, @@ -907,6 +755,7 @@ static class MetricsOptionGroup { PluginsConfigurationOptions pluginsConfigurationOptions = new PluginsConfigurationOptions(); private EthNetworkConfig ethNetworkConfig; + private P2PConfiguration p2pConfiguration; private JsonRpcConfiguration jsonRpcConfiguration; private JsonRpcConfiguration engineJsonRpcConfiguration; private GraphQLConfiguration graphQLConfiguration; @@ -1327,13 +1176,9 @@ private void preSynchronization() { private Runner buildRunner() { return synchronize( besuController, - p2PDiscoveryOptionGroup.p2pEnabled, + p2pConfiguration, p2pTLSConfiguration, - p2PDiscoveryOptionGroup.peerDiscoveryEnabled, ethNetworkConfig, - p2PDiscoveryOptionGroup.p2pHost, - p2PDiscoveryOptionGroup.p2pInterface, - p2PDiscoveryOptionGroup.p2pPort, graphQLConfiguration, jsonRpcConfiguration, engineJsonRpcConfiguration, @@ -1530,12 +1375,11 @@ private void configureNativeLibs() { private void validateOptions() { validateRequiredOptions(); issueOptionWarnings(); - validateP2PInterface(p2PDiscoveryOptionGroup.p2pInterface); + validateP2POptions(); validateMiningParams(); validateNatParams(); validateNetStatsParams(); validateDnsOptionsParams(); - ensureValidPeerBoundParams(); validateRpcOptionsParams(); validateRpcWsOptions(); validateChainDataPruningParams(); @@ -1565,6 +1409,16 @@ private void validateConsensusSyncCompatibilityOptions() { } } + private void validateP2POptions() { + p2POptions.validate(commandLine, logger); + validateP2PInterface(); + } + + /** Validates P2P interface IP address/host name. Visible for testing. */ + protected void validateP2PInterface() { + p2POptions.validateP2PInterface(commandLine, logger); + } + private void validateApiOptions() { apiConfigurationOptions.validate(commandLine, logger); } @@ -1595,22 +1449,6 @@ private void validateMiningParams() { commandLine, genesisConfigOptionsSupplier.get(), isMergeEnabled(), logger); } - /** - * Validates P2P interface IP address/host name. Visible for testing. - * - * @param p2pInterface IP Address/host name - */ - 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 (final UnknownHostException | SocketException e) { - throw new ParameterException(commandLine, failMessage, e); - } - } - private void validateGraphQlOptions() { graphQlOptions.validate(logger, commandLine); } @@ -1653,23 +1491,6 @@ private void validateDnsOptionsParams() { } } - private void ensureValidPeerBoundParams() { - maxPeers = p2PDiscoveryOptionGroup.maxPeers; - final Boolean isLimitRemoteWireConnectionsEnabled = - p2PDiscoveryOptionGroup.isLimitRemoteWireConnectionsEnabled; - if (isLimitRemoteWireConnectionsEnabled) { - final float fraction = - Fraction.fromPercentage(p2PDiscoveryOptionGroup.maxRemoteConnectionsPercentage) - .getValue(); - checkState( - fraction >= 0.0 && fraction <= 1.0, - "Fraction of remote connections allowed must be between 0.0 and 1.0 (inclusive)."); - maxRemoteInitiatedPeers = Math.round(fraction * maxPeers); - } else { - maxRemoteInitiatedPeers = maxPeers; - } - } - private void validateRpcOptionsParams() { final Predicate configuredApis = apiName -> @@ -1716,24 +1537,6 @@ private GenesisConfigOptions readGenesisConfigOptions() { } private void issueOptionWarnings() { - - // Check that P2P options are able to work - CommandLineUtils.checkOptionDependencies( - logger, - commandLine, - "--p2p-enabled", - !p2PDiscoveryOptionGroup.p2pEnabled, - asList( - "--bootnodes", - "--discovery-enabled", - "--max-peers", - "--banned-node-id", - "--banned-node-ids", - "--p2p-host", - "--p2p-interface", - "--p2p-port", - "--remote-connections-max-percentage")); - if (SyncMode.isFullSync(getDefaultSyncModeIfNotSet()) && isOptionSet(commandLine, "--sync-min-peers")) { logger.warn("--sync-min-peers is ignored in FULL sync-mode"); @@ -1763,17 +1566,18 @@ && isOptionSet(commandLine, "--sync-min-peers")) { } private void configure() throws Exception { + p2pConfiguration = p2POptions.toDomainObject(); + checkPortClash(); checkIfRequiredPortsAreAvailable(); syncMode = getDefaultSyncModeIfNotSet(); versionCompatibilityProtection = getDefaultVersionCompatibilityProtectionIfNotSet(); - ethNetworkConfig = updateNetworkConfig(network); - + ethNetworkConfig = updateNetworkConfig(network, p2pConfiguration); jsonRpcConfiguration = jsonRpcHttpOptions.jsonRpcConfiguration( hostsAllowlist, - p2PDiscoveryOptionGroup.autoDiscoverDefaultIP().getHostAddress(), + autoDiscoverDefaultIP().getHostAddress(), unstableRPCOptions.getHttpTimeoutSec()); if (isEngineApiEnabled()) { engineJsonRpcConfiguration = @@ -1784,12 +1588,12 @@ private void configure() throws Exception { graphQLConfiguration = graphQlOptions.graphQLConfiguration( hostsAllowlist, - p2PDiscoveryOptionGroup.autoDiscoverDefaultIP().getHostAddress(), + autoDiscoverDefaultIP().getHostAddress(), unstableRPCOptions.getHttpTimeoutSec()); webSocketConfiguration = rpcWebsocketOptions.webSocketConfiguration( hostsAllowlist, - p2PDiscoveryOptionGroup.autoDiscoverDefaultIP().getHostAddress(), + autoDiscoverDefaultIP().getHostAddress(), unstableRPCOptions.getWsTimeoutSec()); jsonRpcIpcConfiguration = jsonRpcIpcConfiguration( @@ -1886,8 +1690,11 @@ public BesuControllerBuilder getControllerBuilder() { .withMiningParameters(miningParametersSupplier.get()) .withJsonRpcHttpOptions(jsonRpcHttpOptions); final KeyValueStorageProvider storageProvider = keyValueStorageProvider(keyValueStorageName); + final P2PConfiguration p2PConfiguration = p2POptions.toDomainObject(); return controllerBuilderFactory - .fromEthNetworkConfig(updateNetworkConfig(network), getDefaultSyncModeIfNotSet()) + .fromEthNetworkConfig( + updateNetworkConfig(network, p2PConfiguration), getDefaultSyncModeIfNotSet()) + .p2PConfiguration(p2PConfiguration) .synchronizerConfiguration(buildSyncConfig()) .ethProtocolConfiguration(unstableEthProtocolOptions.toDomainObject()) .networkConfiguration(unstableNetworkingOptions.toDomainObject()) @@ -1911,9 +1718,6 @@ public BesuControllerBuilder getControllerBuilder() { .requiredBlocks(requiredBlocks) .reorgLoggingThreshold(reorgLoggingThreshold) .evmConfiguration(unstableEvmOptions.toDomainObject()) - .maxPeers(p2PDiscoveryOptionGroup.maxPeers) - .maxRemotelyInitiatedPeers(maxRemoteInitiatedPeers) - .randomPeerPriority(p2PDiscoveryOptionGroup.randomPeerPriority) .chainPruningConfiguration(unstableChainPruningOptions.toDomainObject()) .cacheLastBlocks(numberOfblocksToCache) .genesisStateHashCacheEnabled(genesisStateHashCacheEnabled); @@ -1925,7 +1729,7 @@ private JsonRpcConfiguration createEngineJsonRpcConfiguration( final JsonRpcConfiguration engineConfig = jsonRpcHttpOptions.jsonRpcConfiguration( allowCallsFrom, - p2PDiscoveryOptionGroup.autoDiscoverDefaultIP().getHostAddress(), + autoDiscoverDefaultIP().getHostAddress(), unstableRPCOptions.getWsTimeoutSec()); engineConfig.setPort(engineListenPort); engineConfig.setRpcApis(Arrays.asList("ENGINE", "ETH")); @@ -1992,7 +1796,7 @@ public MetricsConfiguration metricsConfiguration() { .enabled(metricsOptionGroup.isMetricsEnabled) .host( Strings.isNullOrEmpty(metricsOptionGroup.metricsHost) - ? p2PDiscoveryOptionGroup.autoDiscoverDefaultIP().getHostAddress() + ? autoDiscoverDefaultIP().getHostAddress() : metricsOptionGroup.metricsHost) .port(metricsOptionGroup.metricsPort) .protocol(metricsOptionGroup.metricsProtocol) @@ -2000,7 +1804,7 @@ public MetricsConfiguration metricsConfiguration() { .pushEnabled(metricsOptionGroup.isMetricsPushEnabled) .pushHost( Strings.isNullOrEmpty(metricsOptionGroup.metricsPushHost) - ? p2PDiscoveryOptionGroup.autoDiscoverDefaultIP().getHostAddress() + ? autoDiscoverDefaultIP().getHostAddress() : metricsOptionGroup.metricsPushHost) .pushPort(metricsOptionGroup.metricsPushPort) .pushInterval(metricsOptionGroup.metricsPushInterval) @@ -2264,13 +2068,9 @@ private OptionalInt getGenesisBlockPeriodSeconds( // Blockchain synchronization from peers. private Runner synchronize( final BesuController controller, - final boolean p2pEnabled, + final P2PConfiguration p2pConfiguration, final Optional p2pTLSConfiguration, - final boolean peerDiscoveryEnabled, final EthNetworkConfig ethNetworkConfig, - final String p2pAdvertisedHost, - final String p2pListenInterface, - final int p2pListenPort, final GraphQLConfiguration graphQLConfiguration, final JsonRpcConfiguration jsonRpcConfiguration, final JsonRpcConfiguration engineJsonRpcConfiguration, @@ -2291,16 +2091,12 @@ private Runner synchronize( runnerBuilder .vertx(vertx) .besuController(controller) - .p2pEnabled(p2pEnabled) + .p2PConfiguration(p2pConfiguration) .natMethod(natMethod) .natManagerServiceName(unstableNatOptions.getNatManagerServiceName()) .natMethodFallbackEnabled(unstableNatOptions.getNatMethodFallbackEnabled()) - .discovery(peerDiscoveryEnabled) .ethNetworkConfig(ethNetworkConfig) .permissioningConfiguration(permissioningConfiguration) - .p2pAdvertisedHost(p2pAdvertisedHost) - .p2pListenInterface(p2pListenInterface) - .p2pListenPort(p2pListenPort) .networkingConfiguration(unstableNetworkingOptions.toDomainObject()) .legacyForkId(unstableEthProtocolOptions.toDomainObject().isLegacyEth64ForkIdEnabled()) .graphQLConfiguration(graphQLConfiguration) @@ -2311,7 +2107,6 @@ private Runner synchronize( .apiConfiguration(apiConfiguration) .pidPath(pidPath) .dataDir(dataDir()) - .bannedNodeIds(p2PDiscoveryOptionGroup.bannedNodeIds) .metricsSystem(metricsSystem) .permissioningService(permissioningService) .metricsConfiguration(metricsConfiguration) @@ -2323,7 +2118,6 @@ private Runner synchronize( .storageProvider(keyValueStorageProvider(keyValueStorageName)) .rpcEndpointService(rpcEndpointServiceImpl) .enodeDnsConfiguration(getEnodeDnsConfiguration()) - .allowedSubnets(p2PDiscoveryOptionGroup.allowedSubnets) .build(); addShutdownHook(runner); @@ -2367,7 +2161,8 @@ private void addShutdownHook(final Runner runner) { "BesuCommand-Shutdown-Hook")); } - private EthNetworkConfig updateNetworkConfig(final NetworkName network) { + private EthNetworkConfig updateNetworkConfig( + final NetworkName network, final P2PConfiguration p2pConfig) { final EthNetworkConfig.Builder builder = new EthNetworkConfig.Builder(EthNetworkConfig.getNetworkConfig(network)); @@ -2398,7 +2193,7 @@ private EthNetworkConfig updateNetworkConfig(final NetworkName network) { } } - if (p2PDiscoveryOptionGroup.bootNodes == null) { + if (p2pConfig.getBootNodes() == null) { builder.setBootNodes(new ArrayList<>()); } builder.setDnsDiscoveryUrl(null); @@ -2410,8 +2205,8 @@ private EthNetworkConfig updateNetworkConfig(final NetworkName network) { builder.setNetworkId(networkId); } - if (p2PDiscoveryOptionGroup.discoveryDnsUrl != null) { - builder.setDnsDiscoveryUrl(p2PDiscoveryOptionGroup.discoveryDnsUrl); + if (p2pConfig.getDiscoveryDnsUrl() != null) { + builder.setDnsDiscoveryUrl(p2pConfig.getDiscoveryDnsUrl()); } else { final Optional discoveryDnsUrlFromGenesis = genesisConfigOptionsSupplier.get().getDiscoveryOptions().getDiscoveryDnsUrl(); @@ -2419,9 +2214,9 @@ private EthNetworkConfig updateNetworkConfig(final NetworkName network) { } List listBootNodes = null; - if (p2PDiscoveryOptionGroup.bootNodes != null) { + if (p2pConfig.getBootNodes() != null) { try { - listBootNodes = buildEnodes(p2PDiscoveryOptionGroup.bootNodes, getEnodeDnsConfiguration()); + listBootNodes = buildEnodes(p2pConfig.getBootNodes(), getEnodeDnsConfiguration()); } catch (final IllegalArgumentException e) { throw new ParameterException(commandLine, e.getMessage()); } @@ -2433,7 +2228,7 @@ private EthNetworkConfig updateNetworkConfig(final NetworkName network) { } } if (listBootNodes != null) { - if (!p2PDiscoveryOptionGroup.peerDiscoveryEnabled) { + if (!p2pConfig.isDiscoveryEnabled()) { logger.warn("Discovery disabled: bootnodes will be ignored."); } DiscoveryConfiguration.assertValidBootnodes(listBootNodes); @@ -2568,12 +2363,12 @@ protected void checkIfRequiredPortsAreAvailable() { .filter(port -> port > 0) .forEach( port -> { - if (port.equals(p2PDiscoveryOptionGroup.p2pPort) + if (port.equals(p2pConfiguration.getPort()) && (NetworkUtility.isPortUnavailableForTcp(port) || NetworkUtility.isPortUnavailableForUdp(port))) { unavailablePorts.add(port); } - if (!port.equals(p2PDiscoveryOptionGroup.p2pPort) + if (!port.equals(p2pConfiguration.getPort()) && NetworkUtility.isPortUnavailableForTcp(port)) { unavailablePorts.add(port); } @@ -2593,8 +2388,7 @@ protected void checkIfRequiredPortsAreAvailable() { */ private List getEffectivePorts() { final List effectivePorts = new ArrayList<>(); - addPortIfEnabled( - effectivePorts, p2PDiscoveryOptionGroup.p2pPort, p2PDiscoveryOptionGroup.p2pEnabled); + addPortIfEnabled(effectivePorts, p2pConfiguration.getPort(), p2pConfiguration.isP2pEnabled()); addPortIfEnabled( effectivePorts, graphQlOptions.getGraphQLHttpPort(), graphQlOptions.isGraphQLHttpEnabled()); addPortIfEnabled( diff --git a/besu/src/main/java/org/hyperledger/besu/cli/options/stable/P2POptions.java b/besu/src/main/java/org/hyperledger/besu/cli/options/stable/P2POptions.java new file mode 100644 index 00000000000..805514071bd --- /dev/null +++ b/besu/src/main/java/org/hyperledger/besu/cli/options/stable/P2POptions.java @@ -0,0 +1,251 @@ +/* + * Copyright contributors to Hyperledger Besu. + * + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.cli.options.stable; + +import static java.util.Arrays.asList; +import static org.hyperledger.besu.ethereum.p2p.config.AutoDiscoverDefaultIP.autoDiscoverDefaultIP; + +import org.hyperledger.besu.cli.DefaultCommandValues; +import org.hyperledger.besu.cli.converter.PercentageConverter; +import org.hyperledger.besu.cli.converter.SubnetInfoConverter; +import org.hyperledger.besu.cli.options.CLIOptions; +import org.hyperledger.besu.cli.util.CommandLineUtils; +import org.hyperledger.besu.ethereum.p2p.config.P2PConfiguration; +import org.hyperledger.besu.ethereum.p2p.peers.EnodeURLImpl; +import org.hyperledger.besu.util.NetworkUtility; +import org.hyperledger.besu.util.number.Fraction; +import org.hyperledger.besu.util.number.Percentage; + +import java.net.SocketException; +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import org.apache.commons.net.util.SubnetUtils.SubnetInfo; +import org.apache.tuweni.bytes.Bytes; +import org.slf4j.Logger; +import picocli.CommandLine; + +/** The P2POptions Config Cli Options. */ +public class P2POptions implements CLIOptions { + + // Completely disables P2P within Besu. + @CommandLine.Option( + names = {"--p2p-enabled"}, + description = "Enable P2P functionality (default: ${DEFAULT-VALUE})", + arity = "1") + private final Boolean p2pEnabled = true; + + // Boolean option to indicate if peers should NOT be discovered, default to + // false indicates that + // the peers should be discovered by default. + // + // This negative option is required because of the nature of the option that is + // true when + // added on the command line. You can't do --option=false, so false is set as + // default + // and you have not to set the option at all if you want it false. + // This seems to be the only way it works with Picocli. + // Also many other software use the same negative option scheme for false + // defaults + // meaning that it's probably the right way to handle disabling options. + @CommandLine.Option( + names = {"--discovery-enabled"}, + description = "Enable P2P discovery (default: ${DEFAULT-VALUE})", + arity = "1") + private final Boolean peerDiscoveryEnabled = true; + + // A list of bootstrap nodes can be passed + // and a hardcoded list will be used otherwise by the Runner. + // NOTE: we have no control over default value here. + @CommandLine.Option( + names = {"--bootnodes"}, + paramLabel = "", + description = + "Comma separated enode URLs for P2P discovery bootstrap. " + + "Default is a predefined list.", + split = ",", + arity = "0..*") + private final List bootNodes = null; + + @SuppressWarnings({"FieldCanBeFinal", "FieldMayBeFinal"}) // PicoCLI requires non-final Strings. + @CommandLine.Option( + names = {"--p2p-host"}, + paramLabel = DefaultCommandValues.MANDATORY_HOST_FORMAT_HELP, + description = "IP address this node advertises to its peers (default: ${DEFAULT-VALUE})", + arity = "1") + private String p2pHost = autoDiscoverDefaultIP().getHostAddress(); + + @SuppressWarnings({"FieldCanBeFinal", "FieldMayBeFinal"}) // PicoCLI requires non-final Strings. + @CommandLine.Option( + names = {"--p2p-interface"}, + paramLabel = DefaultCommandValues.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; + + @CommandLine.Option( + names = {"--p2p-port"}, + paramLabel = DefaultCommandValues.MANDATORY_PORT_FORMAT_HELP, + description = "Port on which to listen for P2P communication (default: ${DEFAULT-VALUE})", + arity = "1") + private final Integer p2pPort = EnodeURLImpl.DEFAULT_LISTENING_PORT; + + @CommandLine.Option( + names = {"--max-peers", "--p2p-peer-upper-bound"}, + paramLabel = DefaultCommandValues.MANDATORY_INTEGER_FORMAT_HELP, + description = "Maximum P2P connections that can be established (default: ${DEFAULT-VALUE})") + private final Integer maxPeers = DefaultCommandValues.DEFAULT_MAX_PEERS; + + @CommandLine.Option( + names = {"--remote-connections-limit-enabled"}, + description = + "Whether to limit the number of P2P connections initiated remotely. (default: ${DEFAULT-VALUE})") + private final Boolean isLimitRemoteWireConnectionsEnabled = true; + + @CommandLine.Option( + names = {"--remote-connections-max-percentage"}, + paramLabel = DefaultCommandValues.MANDATORY_DOUBLE_FORMAT_HELP, + description = + "The maximum percentage of P2P connections that can be initiated remotely. Must be between 0 and 100 inclusive. (default: ${DEFAULT-VALUE})", + arity = "1", + converter = PercentageConverter.class) + private final Percentage maxRemoteConnectionsPercentage = + Fraction.fromFloat(DefaultCommandValues.DEFAULT_FRACTION_REMOTE_WIRE_CONNECTIONS_ALLOWED) + .toPercentage(); + + @SuppressWarnings({"FieldCanBeFinal", "FieldMayBeFinal"}) // PicoCLI requires non-final Strings. + @CommandLine.Option( + names = {"--discovery-dns-url"}, + description = "Specifies the URL to use for DNS discovery") + private String discoveryDnsUrl = null; + + @CommandLine.Option( + names = {"--random-peer-priority-enabled"}, + description = + "Allow for incoming connections to be prioritized randomly. This will prevent (typically small, stable) networks from forming impenetrable peer cliques. (default: ${DEFAULT-VALUE})") + private final Boolean randomPeerPriority = Boolean.FALSE; + + @CommandLine.Option( + names = {"--banned-node-ids", "--banned-node-id"}, + paramLabel = DefaultCommandValues.MANDATORY_NODE_ID_FORMAT_HELP, + description = "A list of node IDs to ban from the P2P network.", + split = ",", + arity = "1..*") + void setBannedNodeIds(final List values) { + try { + bannedNodeIds = + values.stream() + .filter(value -> !value.isEmpty()) + .map(EnodeURLImpl::parseNodeId) + .collect(Collectors.toList()); + } catch (final IllegalArgumentException e) { + throw new CommandLine.ParameterException( + new CommandLine(this), "Invalid ids supplied to '--banned-node-ids'. " + e.getMessage()); + } + } + + @CommandLine.Option( + names = {"--net-restrict"}, + arity = "1..*", + split = ",", + converter = SubnetInfoConverter.class, + description = + "Comma-separated list of allowed IP subnets (e.g., '192.168.1.0/24,10.0.0.0/8').") + private List allowedSubnets; + + private List bannedNodeIds = new ArrayList<>(); + + /** Default constructor. */ + public P2POptions() {} + + /** + * Validates P2P options + * + * @param commandLine the commandLine + * @param logger the logger + */ + public void validate(final CommandLine commandLine, final Logger logger) { + final float fraction = Fraction.fromPercentage(maxRemoteConnectionsPercentage).getValue(); + if (!(fraction >= 0.0 && fraction <= 1.0)) { + throw new CommandLine.ParameterException( + commandLine, + "Fraction of remote connections allowed must be between 0.0 and 1.0 (inclusive)."); + } + checkP2pOptionsDependencies(commandLine, logger); + } + + /** + * Validates P2P interface IP address/host name. Visible for testing. + * + * @param commandLine the commandLine + * @param logger the logger + */ + public void validateP2PInterface(final CommandLine commandLine, final Logger logger) { + final String failMessage = "The provided --p2p-interface is not available: " + p2pInterface; + try { + if (!NetworkUtility.isNetworkInterfaceAvailable(p2pInterface)) { + throw new CommandLine.ParameterException(commandLine, failMessage); + } + } catch (final UnknownHostException | SocketException e) { + throw new CommandLine.ParameterException(commandLine, failMessage, e); + } + } + + private void checkP2pOptionsDependencies(final CommandLine commandLine, final Logger logger) { + // Check that P2P options are able to work + CommandLineUtils.checkOptionDependencies( + logger, + commandLine, + "--p2p-enabled", + !p2pEnabled, + asList( + "--bootnodes", + "--discovery-enabled", + "--max-peers", + "--banned-node-id", + "--banned-node-ids", + "--p2p-host", + "--p2p-interface", + "--p2p-port", + "--remote-connections-max-percentage")); + } + + @Override + public P2PConfiguration toDomainObject() { + return new P2PConfiguration.Builder() + .p2pEnabled(p2pEnabled) + .discoveryEnabled(peerDiscoveryEnabled) + .bootNodes(bootNodes) + .host(p2pHost) + .p2pInterface(p2pInterface) + .port(p2pPort) + .maxPeers(maxPeers) + .isLimitRemoteWireConnectionsEnabled(isLimitRemoteWireConnectionsEnabled) + .maxRemoteConnectionsPercentage(maxRemoteConnectionsPercentage) + .discoveryDnsUrl(discoveryDnsUrl) + .randomPeerPriority(randomPeerPriority) + .bannedNodeIds(bannedNodeIds) + .allowedSubnets(allowedSubnets) + .build(); + } + + @Override + public List getCLIOptions() { + return CommandLineUtils.getCLIOptions(this, new P2POptions()); + } +} diff --git a/besu/src/main/java/org/hyperledger/besu/controller/BesuControllerBuilder.java b/besu/src/main/java/org/hyperledger/besu/controller/BesuControllerBuilder.java index ddb75fcdedc..619380509ec 100644 --- a/besu/src/main/java/org/hyperledger/besu/controller/BesuControllerBuilder.java +++ b/besu/src/main/java/org/hyperledger/besu/controller/BesuControllerBuilder.java @@ -79,6 +79,7 @@ import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec; import org.hyperledger.besu.ethereum.p2p.config.NetworkingConfiguration; +import org.hyperledger.besu.ethereum.p2p.config.P2PConfiguration; import org.hyperledger.besu.ethereum.p2p.config.SubProtocolConfiguration; import org.hyperledger.besu.ethereum.storage.StorageProvider; import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueSegmentIdentifier; @@ -184,19 +185,16 @@ public abstract class BesuControllerBuilder implements MiningParameterOverrides /** The Evm configuration. */ protected EvmConfiguration evmConfiguration; - /** The Max peers. */ - protected int maxPeers; + /** The P2P configuration. */ + protected P2PConfiguration p2PConfiguration = P2PConfiguration.createDefault(); /** Manages a cache of bad blocks globally */ protected final BadBlockManager badBlockManager = new BadBlockManager(); - private int maxRemotelyInitiatedPeers; - /** The Chain pruner configuration. */ protected ChainPrunerConfiguration chainPrunerConfiguration = ChainPrunerConfiguration.DEFAULT; private NetworkingConfiguration networkingConfiguration; - private Boolean randomPeerPriority; /** the Dagger configured context that can provide dependencies */ protected Optional besuComponent = Optional.empty(); @@ -448,24 +446,13 @@ public BesuControllerBuilder evmConfiguration(final EvmConfiguration evmConfigur } /** - * Max peers besu controller builder. - * - * @param maxPeers the max peers - * @return the besu controller builder - */ - public BesuControllerBuilder maxPeers(final int maxPeers) { - this.maxPeers = maxPeers; - return this; - } - - /** - * Maximum number of remotely initiated peer connections + * P2P Configuration besu controller builder. * - * @param maxRemotelyInitiatedPeers maximum number of remotely initiated peer connections + * @param p2PConfiguration p2PConfiguration * @return the besu controller builder */ - public BesuControllerBuilder maxRemotelyInitiatedPeers(final int maxRemotelyInitiatedPeers) { - this.maxRemotelyInitiatedPeers = maxRemotelyInitiatedPeers; + public BesuControllerBuilder p2PConfiguration(final P2PConfiguration p2PConfiguration) { + this.p2PConfiguration = p2PConfiguration; return this; } @@ -504,17 +491,6 @@ public BesuControllerBuilder networkConfiguration( return this; } - /** - * sets the randomPeerPriority flag in the builder - * - * @param randomPeerPriority the random peer priority flag - * @return the besu controller builder - */ - public BesuControllerBuilder randomPeerPriority(final Boolean randomPeerPriority) { - this.randomPeerPriority = randomPeerPriority; - return this; - } - /** * Sets whether parallel transaction processing is enabled. When parallel transaction processing * is enabled, transactions within a block can be processed in parallel and potentially improving @@ -619,9 +595,9 @@ public BesuController build() { maxMessageSize, messagePermissioningProviders, nodeKey.getPublicKey().getEncodedBytes(), - maxPeers, - maxRemotelyInitiatedPeers, - randomPeerPriority, + p2PConfiguration.getMaxPeers(), + p2PConfiguration.getMaxRemoteInitiatedPeers(), + p2PConfiguration.isRandomPeerPriority(), syncConfig.getSyncMode(), forkIdManager); diff --git a/besu/src/test/java/org/hyperledger/besu/RunnerBuilderTest.java b/besu/src/test/java/org/hyperledger/besu/RunnerBuilderTest.java index c1770640801..d1e8589dadd 100644 --- a/besu/src/test/java/org/hyperledger/besu/RunnerBuilderTest.java +++ b/besu/src/test/java/org/hyperledger/besu/RunnerBuilderTest.java @@ -59,6 +59,7 @@ import org.hyperledger.besu.ethereum.mainnet.MainnetBlockHeaderFunctions; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.p2p.config.NetworkingConfiguration; +import org.hyperledger.besu.ethereum.p2p.config.P2PConfiguration; import org.hyperledger.besu.ethereum.p2p.config.SubProtocolConfiguration; import org.hyperledger.besu.ethereum.p2p.peers.EnodeURLImpl; import org.hyperledger.besu.ethereum.storage.StorageProvider; @@ -150,11 +151,14 @@ public void enodeUrlShouldHaveAdvertisedHostWhenDiscoveryDisabled() { final Runner runner = new RunnerBuilder() - .p2pListenInterface("0.0.0.0") - .p2pListenPort(p2pListenPort) - .p2pAdvertisedHost(p2pAdvertisedHost) - .p2pEnabled(true) - .discovery(false) + .p2PConfiguration( + P2PConfiguration.builder() + .p2pInterface("0.0.0.0") + .port(p2pListenPort) + .host(p2pAdvertisedHost) + .p2pEnabled(true) + .discoveryEnabled(false) + .build()) .besuController(besuController) .ethNetworkConfig(mock(EthNetworkConfig.class)) .metricsSystem(mock(ObservableMetricsSystem.class)) @@ -194,11 +198,14 @@ public void movingAcrossProtocolSpecsUpdatesNodeRecord() { when(protocolContext.getBlockchain()).thenReturn(inMemoryBlockchain); final Runner runner = new RunnerBuilder() - .discovery(true) - .p2pListenInterface("0.0.0.0") - .p2pListenPort(p2pListenPort) - .p2pAdvertisedHost(p2pAdvertisedHost) - .p2pEnabled(true) + .p2PConfiguration( + P2PConfiguration.builder() + .p2pInterface("0.0.0.0") + .port(p2pListenPort) + .host(p2pAdvertisedHost) + .p2pEnabled(true) + .discoveryEnabled(true) + .build()) .natMethod(NatMethod.NONE) .besuController(besuController) .ethNetworkConfig(mock(EthNetworkConfig.class)) @@ -252,11 +259,14 @@ public void whenEngineApiAddedListensOnDefaultPort() { final Runner runner = new RunnerBuilder() - .discovery(true) - .p2pListenInterface("0.0.0.0") - .p2pListenPort(30303) - .p2pAdvertisedHost("127.0.0.1") - .p2pEnabled(true) + .p2PConfiguration( + P2PConfiguration.builder() + .p2pInterface("0.0.0.0") + .port(30303) + .host("127.0.0.1") + .p2pEnabled(true) + .discoveryEnabled(true) + .build()) .natMethod(NatMethod.NONE) .besuController(besuController) .ethNetworkConfig(mockMainnet) @@ -295,11 +305,14 @@ public void whenEngineApiAddedWebSocketReadyOnSamePort() { final Runner runner = new RunnerBuilder() - .discovery(true) - .p2pListenInterface("0.0.0.0") - .p2pListenPort(30303) - .p2pAdvertisedHost("127.0.0.1") - .p2pEnabled(true) + .p2PConfiguration( + P2PConfiguration.builder() + .p2pInterface("0.0.0.0") + .port(30303) + .host("127.0.0.1") + .p2pEnabled(true) + .discoveryEnabled(true) + .build()) .natMethod(NatMethod.NONE) .besuController(besuController) .ethNetworkConfig(mockMainnet) @@ -337,11 +350,14 @@ public void whenEngineApiAddedEthSubscribeAvailable() { final Runner runner = new RunnerBuilder() - .discovery(true) - .p2pListenInterface("0.0.0.0") - .p2pListenPort(30303) - .p2pAdvertisedHost("127.0.0.1") - .p2pEnabled(true) + .p2PConfiguration( + P2PConfiguration.builder() + .p2pInterface("0.0.0.0") + .port(30303) + .host("127.0.0.1") + .p2pEnabled(true) + .discoveryEnabled(true) + .build()) .natMethod(NatMethod.NONE) .besuController(besuController) .ethNetworkConfig(mockMainnet) @@ -381,11 +397,14 @@ public void noEngineApiNoServiceForMethods() { final Runner runner = new RunnerBuilder() - .discovery(true) - .p2pListenInterface("0.0.0.0") - .p2pListenPort(30303) - .p2pAdvertisedHost("127.0.0.1") - .p2pEnabled(true) + .p2PConfiguration( + P2PConfiguration.builder() + .p2pInterface("0.0.0.0") + .port(30303) + .host("127.0.0.1") + .p2pEnabled(true) + .discoveryEnabled(true) + .build()) .natMethod(NatMethod.NONE) .besuController(besuController) .ethNetworkConfig(mockMainnet) diff --git a/besu/src/test/java/org/hyperledger/besu/RunnerTest.java b/besu/src/test/java/org/hyperledger/besu/RunnerTest.java index e10d5475818..eedb1932d9f 100644 --- a/besu/src/test/java/org/hyperledger/besu/RunnerTest.java +++ b/besu/src/test/java/org/hyperledger/besu/RunnerTest.java @@ -54,6 +54,7 @@ import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec; import org.hyperledger.besu.ethereum.p2p.config.NetworkingConfiguration; +import org.hyperledger.besu.ethereum.p2p.config.P2PConfiguration; import org.hyperledger.besu.ethereum.p2p.peers.EnodeURLImpl; import org.hyperledger.besu.ethereum.storage.StorageProvider; import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueSegmentIdentifier; @@ -192,9 +193,7 @@ private void syncFromGenesis(final SyncMode mode, final GenesisConfigFile genesi final RunnerBuilder runnerBuilder = new RunnerBuilder() .vertx(vertx) - .discovery(true) - .p2pAdvertisedHost(listenHost) - .p2pListenPort(0) + .p2PConfiguration(P2PConfiguration.builder().host(listenHost).port(0).build()) .metricsSystem(noOpMetricsSystem) .permissioningService(new PermissioningServiceImpl()) .staticNodes(emptySet()) @@ -480,9 +479,12 @@ private BesuController getController( .gasLimitCalculator(GasLimitCalculator.constant()) .evmConfiguration(EvmConfiguration.DEFAULT) .networkConfiguration(NetworkingConfiguration.create()) - .randomPeerPriority(Boolean.FALSE) - .maxPeers(25) - .maxRemotelyInitiatedPeers(15) + .p2PConfiguration( + P2PConfiguration.builder() + .randomPeerPriority(false) + .maxPeers(25) + .randomPeerPriority(false) + .build()) .build(); } } diff --git a/besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java b/besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java index efeff412d62..d113a216d78 100644 --- a/besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java +++ b/besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java @@ -58,6 +58,7 @@ import org.hyperledger.besu.ethereum.core.MiningParameters; import org.hyperledger.besu.ethereum.eth.sync.SyncMode; import org.hyperledger.besu.ethereum.eth.sync.SynchronizerConfiguration; +import org.hyperledger.besu.ethereum.p2p.config.P2PConfiguration; import org.hyperledger.besu.ethereum.p2p.peers.EnodeURLImpl; import org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration; import org.hyperledger.besu.evm.precompile.AbstractAltBnPrecompiledContract; @@ -111,6 +112,7 @@ public class BesuCommandTest extends CommandTestAbstract { private static final WebSocketConfiguration DEFAULT_WEB_SOCKET_CONFIGURATION; private static final MetricsConfiguration DEFAULT_METRICS_CONFIGURATION; private static final ApiConfiguration DEFAULT_API_CONFIGURATION; + private static final P2PConfiguration DEFAULT_P2P_CONFIGURATION; private static final int GENESIS_CONFIG_TEST_CHAINID = 3141592; private static final JsonObject GENESIS_VALID_JSON = @@ -150,6 +152,7 @@ public class BesuCommandTest extends CommandTestAbstract { DEFAULT_WEB_SOCKET_CONFIGURATION = WebSocketConfiguration.createDefault(); DEFAULT_METRICS_CONFIGURATION = MetricsConfiguration.builder().build(); DEFAULT_API_CONFIGURATION = ImmutableApiConfiguration.builder().build(); + DEFAULT_P2P_CONFIGURATION = P2PConfiguration.createDefault(); } @BeforeEach @@ -197,11 +200,8 @@ public void callingVersionDisplayBesuInfoVersion() { public void callingBesuCommandWithoutOptionsMustSyncWithDefaultValues() { parseCommand(); - final int maxPeers = 25; - final ArgumentCaptor ethNetworkArg = ArgumentCaptor.forClass(EthNetworkConfig.class); - verify(mockRunnerBuilder).discovery(eq(true)); verify(mockRunnerBuilder) .ethNetworkConfig( new EthNetworkConfig( @@ -209,15 +209,14 @@ public void callingBesuCommandWithoutOptionsMustSyncWithDefaultValues() { MAINNET.getNetworkId(), MAINNET_BOOTSTRAP_NODES, MAINNET_DISCOVERY_URL)); - verify(mockRunnerBuilder).p2pAdvertisedHost(eq("127.0.0.1")); - verify(mockRunnerBuilder).p2pListenPort(eq(30303)); verify(mockRunnerBuilder).jsonRpcConfiguration(eq(DEFAULT_JSON_RPC_CONFIGURATION)); verify(mockRunnerBuilder).graphQLConfiguration(eq(DEFAULT_GRAPH_QL_CONFIGURATION)); verify(mockRunnerBuilder).webSocketConfiguration(eq(DEFAULT_WEB_SOCKET_CONFIGURATION)); verify(mockRunnerBuilder).metricsConfiguration(eq(DEFAULT_METRICS_CONFIGURATION)); verify(mockRunnerBuilder).ethNetworkConfig(ethNetworkArg.capture()); verify(mockRunnerBuilder).autoLogBloomCaching(eq(true)); - verify(mockRunnerBuilder).apiConfiguration(DEFAULT_API_CONFIGURATION); + verify(mockRunnerBuilder).apiConfiguration(eq(DEFAULT_API_CONFIGURATION)); + verify(mockRunnerBuilder).p2PConfiguration(eq(DEFAULT_P2P_CONFIGURATION)); verify(mockRunnerBuilder).build(); verify(mockControllerBuilderFactory).fromEthNetworkConfig(ethNetworkArg.capture(), any()); @@ -229,8 +228,6 @@ public void callingBesuCommandWithoutOptionsMustSyncWithDefaultValues() { verify(mockControllerBuilder).nodeKey(isNotNull()); verify(mockControllerBuilder).storageProvider(storageProviderArgumentCaptor.capture()); verify(mockControllerBuilder).gasLimitCalculator(eq(GasLimitCalculator.constant())); - verify(mockControllerBuilder).maxPeers(eq(maxPeers)); - verify(mockControllerBuilder).maxRemotelyInitiatedPeers(eq((int) Math.floor(0.6 * maxPeers))); verify(mockControllerBuilder).build(); assertThat(storageProviderArgumentCaptor.getValue()).isNotNull(); @@ -635,9 +632,11 @@ public void identityValueTrueMustBeUsed() { public void p2pEnabledOptionValueTrueMustBeUsed() { parseCommand("--p2p-enabled", "true"); - verify(mockRunnerBuilder).p2pEnabled(eq(true)); + verify(mockRunnerBuilder).p2PConfiguration(p2PConfigurationArgumentCaptor.capture()); verify(mockRunnerBuilder).build(); + assertThat(p2PConfigurationArgumentCaptor.getValue().isP2pEnabled()).isTrue(); + assertThat(commandOutput.toString(UTF_8)).isEmpty(); assertThat(commandErrorOutput.toString(UTF_8)).isEmpty(); } @@ -646,9 +645,11 @@ public void p2pEnabledOptionValueTrueMustBeUsed() { public void p2pEnabledOptionValueFalseMustBeUsed() { parseCommand("--p2p-enabled", "false"); - verify(mockRunnerBuilder).p2pEnabled(eq(false)); + verify(mockRunnerBuilder).p2PConfiguration(p2PConfigurationArgumentCaptor.capture()); verify(mockRunnerBuilder).build(); + assertThat(p2PConfigurationArgumentCaptor.getValue().isP2pEnabled()).isFalse(); + assertThat(commandOutput.toString(UTF_8)).isEmpty(); assertThat(commandErrorOutput.toString(UTF_8)).isEmpty(); } @@ -728,9 +729,11 @@ public void p2pOptionsRequiresServiceToBeEnabledToml() throws IOException { public void discoveryOptionValueTrueMustBeUsed() { parseCommand("--discovery-enabled", "true"); - verify(mockRunnerBuilder).discovery(eq(true)); + verify(mockRunnerBuilder).p2PConfiguration(p2PConfigurationArgumentCaptor.capture()); verify(mockRunnerBuilder).build(); + assertThat(p2PConfigurationArgumentCaptor.getValue().isDiscoveryEnabled()).isTrue(); + assertThat(commandOutput.toString(UTF_8)).isEmpty(); assertThat(commandErrorOutput.toString(UTF_8)).isEmpty(); } @@ -739,9 +742,11 @@ public void discoveryOptionValueTrueMustBeUsed() { public void discoveryOptionValueFalseMustBeUsed() { parseCommand("--discovery-enabled", "false"); - verify(mockRunnerBuilder).discovery(eq(false)); + verify(mockRunnerBuilder).p2PConfiguration(p2PConfigurationArgumentCaptor.capture()); verify(mockRunnerBuilder).build(); + assertThat(p2PConfigurationArgumentCaptor.getValue().isDiscoveryEnabled()).isFalse(); + assertThat(commandOutput.toString(UTF_8)).isEmpty(); assertThat(commandErrorOutput.toString(UTF_8)).isEmpty(); } @@ -906,10 +911,11 @@ public void bannedNodeIdsOptionMustBeUsed() { Arrays.stream(nodes).map(Bytes::toShortHexString).collect(Collectors.joining(",")); parseCommand("--banned-node-ids", nodeIdsArg); - verify(mockRunnerBuilder).bannedNodeIds(bytesCollectionCollector.capture()); + verify(mockRunnerBuilder).p2PConfiguration(p2PConfigurationArgumentCaptor.capture()); verify(mockRunnerBuilder).build(); - assertThat(bytesCollectionCollector.getValue().toArray()).isEqualTo(nodes); + assertThat(p2PConfigurationArgumentCaptor.getValue().getBannedNodeIds().toArray()) + .isEqualTo(nodes); assertThat(commandOutput.toString(UTF_8)).isEmpty(); assertThat(commandErrorOutput.toString(UTF_8)).isEmpty(); @@ -940,12 +946,11 @@ public void p2pHostAndPortOptionsAreRespected() { final int port = 1234; parseCommand("--p2p-host", host, "--p2p-port", String.valueOf(port)); - verify(mockRunnerBuilder).p2pAdvertisedHost(stringArgumentCaptor.capture()); - verify(mockRunnerBuilder).p2pListenPort(intArgumentCaptor.capture()); + verify(mockRunnerBuilder).p2PConfiguration(p2PConfigurationArgumentCaptor.capture()); verify(mockRunnerBuilder).build(); - assertThat(stringArgumentCaptor.getValue()).isEqualTo(host); - assertThat(intArgumentCaptor.getValue()).isEqualTo(port); + assertThat(p2PConfigurationArgumentCaptor.getValue().getHost()).isEqualTo(host); + assertThat(p2PConfigurationArgumentCaptor.getValue().getPort()).isEqualTo(port); assertThat(commandOutput.toString(UTF_8)).isEmpty(); assertThat(commandErrorOutput.toString(UTF_8)).isEmpty(); @@ -957,13 +962,12 @@ public void p2pInterfaceOptionIsRespected() { final String ip = "1.2.3.4"; parseCommand("--p2p-interface", ip); - assertThat(commandOutput.toString(UTF_8)).isEmpty(); - assertThat(commandErrorOutput.toString(UTF_8)).isEmpty(); - - verify(mockRunnerBuilder).p2pListenInterface(stringArgumentCaptor.capture()); + verify(mockRunnerBuilder).p2PConfiguration(p2PConfigurationArgumentCaptor.capture()); verify(mockRunnerBuilder).build(); - assertThat(stringArgumentCaptor.getValue()).isEqualTo(ip); + assertThat(p2PConfigurationArgumentCaptor.getValue().getP2pInterface()).isEqualTo(ip); + assertThat(commandOutput.toString(UTF_8)).isEmpty(); + assertThat(commandErrorOutput.toString(UTF_8)).isEmpty(); } @Test @@ -972,10 +976,10 @@ public void p2pHostMayBeLocalhost() { final String host = "localhost"; parseCommand("--p2p-host", host); - verify(mockRunnerBuilder).p2pAdvertisedHost(stringArgumentCaptor.capture()); + verify(mockRunnerBuilder).p2PConfiguration(p2PConfigurationArgumentCaptor.capture()); verify(mockRunnerBuilder).build(); - assertThat(stringArgumentCaptor.getValue()).isEqualTo(host); + assertThat(p2PConfigurationArgumentCaptor.getValue().getHost()).isEqualTo(host); assertThat(commandOutput.toString(UTF_8)).isEmpty(); assertThat(commandErrorOutput.toString(UTF_8)).isEmpty(); @@ -987,10 +991,10 @@ public void p2pHostMayBeIPv6() { final String host = "2600:DB8::8545"; parseCommand("--p2p-host", host); - verify(mockRunnerBuilder).p2pAdvertisedHost(stringArgumentCaptor.capture()); + verify(mockRunnerBuilder).p2PConfiguration(p2PConfigurationArgumentCaptor.capture()); verify(mockRunnerBuilder).build(); - assertThat(stringArgumentCaptor.getValue()).isEqualTo(host); + assertThat(p2PConfigurationArgumentCaptor.getValue().getHost()).isEqualTo(host); assertThat(commandOutput.toString(UTF_8)).isEmpty(); assertThat(commandErrorOutput.toString(UTF_8)).isEmpty(); @@ -1002,10 +1006,10 @@ public void maxpeersOptionMustBeUsed() { final int maxPeers = 123; parseCommand("--max-peers", String.valueOf(maxPeers)); - verify(mockControllerBuilder).maxPeers(intArgumentCaptor.capture()); - verify(mockControllerBuilder).build(); + verify(mockRunnerBuilder).p2PConfiguration(p2PConfigurationArgumentCaptor.capture()); + verify(mockRunnerBuilder).build(); - assertThat(intArgumentCaptor.getValue()).isEqualTo(maxPeers); + assertThat(p2PConfigurationArgumentCaptor.getValue().getMaxPeers()).isEqualTo(maxPeers); assertThat(commandOutput.toString(UTF_8)).isEmpty(); assertThat(commandErrorOutput.toString(UTF_8)).isEmpty(); @@ -1020,11 +1024,11 @@ public void p2pPeerUpperBound_without_p2pPeerLowerBound_shouldSetMaxPeers() { assertThat(commandOutput.toString(UTF_8)).isEmpty(); assertThat(commandErrorOutput.toString(UTF_8)).isEmpty(); - verify(mockControllerBuilder).maxPeers(intArgumentCaptor.capture()); - assertThat(intArgumentCaptor.getValue()).isEqualTo(maxPeers); - + verify(mockRunnerBuilder).p2PConfiguration(p2PConfigurationArgumentCaptor.capture()); verify(mockRunnerBuilder).build(); + assertThat(p2PConfigurationArgumentCaptor.getValue().getMaxPeers()).isEqualTo(maxPeers); + assertThat(commandOutput.toString(UTF_8)).isEmpty(); assertThat(commandErrorOutput.toString(UTF_8)).isEmpty(); } @@ -1038,12 +1042,13 @@ public void remoteConnectionsPercentageOptionMustBeUsed() { "--remote-connections-max-percentage", String.valueOf(remoteConnectionsPercentage)); - verify(mockControllerBuilder).maxRemotelyInitiatedPeers(intArgumentCaptor.capture()); - verify(mockControllerBuilder).build(); + verify(mockRunnerBuilder).p2PConfiguration(p2PConfigurationArgumentCaptor.capture()); + verify(mockRunnerBuilder).build(); - assertThat(intArgumentCaptor.getValue()) - .isEqualTo( - (int) Math.floor(25 * Fraction.fromPercentage(remoteConnectionsPercentage).getValue())); + var maxRemotelyInitiatedPeers = + (int) Math.floor(25 * Fraction.fromPercentage(remoteConnectionsPercentage).getValue()); + assertThat(p2PConfigurationArgumentCaptor.getValue().getMaxRemoteInitiatedPeers()) + .isEqualTo(maxRemotelyInitiatedPeers); assertThat(commandOutput.toString(UTF_8)).isEmpty(); assertThat(commandErrorOutput.toString(UTF_8)).isEmpty(); @@ -1223,12 +1228,13 @@ public void netRestrictParsedCorrectly() { final String subnet1 = "127.0.0.1/24"; final String subnet2 = "10.0.0.1/24"; parseCommand("--net-restrict", String.join(",", subnet1, subnet2)); - verify(mockRunnerBuilder).allowedSubnets(allowedSubnetsArgumentCaptor.capture()); - assertThat(allowedSubnetsArgumentCaptor.getValue().size()).isEqualTo(2); - assertThat(allowedSubnetsArgumentCaptor.getValue().get(0).getCidrSignature()) - .isEqualTo(subnet1); - assertThat(allowedSubnetsArgumentCaptor.getValue().get(1).getCidrSignature()) - .isEqualTo(subnet2); + verify(mockRunnerBuilder).p2PConfiguration(p2PConfigurationArgumentCaptor.capture()); + verify(mockRunnerBuilder).build(); + + var subnets = p2PConfigurationArgumentCaptor.getValue().getAllowedSubnets(); + assertThat(subnets.size()).isEqualTo(2); + assertThat(subnets.get(0).getCidrSignature()).isEqualTo(subnet1); + assertThat(subnets.get(1).getCidrSignature()).isEqualTo(subnet2); } @Test diff --git a/besu/src/test/java/org/hyperledger/besu/cli/CascadingDefaultProviderTest.java b/besu/src/test/java/org/hyperledger/besu/cli/CascadingDefaultProviderTest.java index 4cdd8163d95..88e2e8f44a9 100644 --- a/besu/src/test/java/org/hyperledger/besu/cli/CascadingDefaultProviderTest.java +++ b/besu/src/test/java/org/hyperledger/besu/cli/CascadingDefaultProviderTest.java @@ -36,6 +36,7 @@ import org.hyperledger.besu.ethereum.core.MiningParameters; import org.hyperledger.besu.ethereum.eth.sync.SyncMode; import org.hyperledger.besu.ethereum.eth.sync.SynchronizerConfiguration; +import org.hyperledger.besu.ethereum.p2p.config.P2PConfiguration; import org.hyperledger.besu.ethereum.p2p.peers.EnodeURLImpl; import org.hyperledger.besu.metrics.prometheus.MetricsConfiguration; import org.hyperledger.besu.plugin.data.EnodeURL; @@ -108,10 +109,12 @@ public void overrideDefaultValuesIfKeyIsPresentInConfigFile(final @TempDir File parseCommand("--config-file", toml.toString()); - verify(mockRunnerBuilder).discovery(eq(false)); + verify(mockRunnerBuilder).p2PConfiguration(p2PConfigurationArgumentCaptor.capture()); + P2PConfiguration p2PConfiguration = p2PConfigurationArgumentCaptor.getValue(); + assertThat(p2PConfiguration.isDiscoveryEnabled()).isFalse(); + assertThat(p2PConfiguration.getHost()).isEqualTo("1.2.3.4"); + assertThat(p2PConfiguration.getPort()).isEqualTo(1234); verify(mockRunnerBuilder).ethNetworkConfig(ethNetworkConfigArgumentCaptor.capture()); - verify(mockRunnerBuilder).p2pAdvertisedHost(eq("1.2.3.4")); - verify(mockRunnerBuilder).p2pListenPort(eq(1234)); verify(mockRunnerBuilder).jsonRpcConfiguration(eq(jsonRpcConfiguration)); verify(mockRunnerBuilder).graphQLConfiguration(eq(graphQLConfiguration)); verify(mockRunnerBuilder).webSocketConfiguration(eq(webSocketConfiguration)); @@ -162,7 +165,12 @@ public void noOverrideDefaultValuesIfKeyIsNotPresentInConfigFile() { final MetricsConfiguration metricsConfiguration = MetricsConfiguration.builder().build(); - verify(mockRunnerBuilder).discovery(eq(true)); + verify(mockRunnerBuilder).p2PConfiguration(p2PConfigurationArgumentCaptor.capture()); + P2PConfiguration p2PConfiguration = p2PConfigurationArgumentCaptor.getValue(); + assertThat(p2PConfiguration.isDiscoveryEnabled()).isTrue(); + assertThat(p2PConfiguration.getHost()).isEqualTo("127.0.0.1"); + assertThat(p2PConfiguration.getPort()).isEqualTo(30303); + verify(mockRunnerBuilder) .ethNetworkConfig( new EthNetworkConfig( @@ -170,8 +178,6 @@ public void noOverrideDefaultValuesIfKeyIsNotPresentInConfigFile() { MAINNET.getNetworkId(), MAINNET_BOOTSTRAP_NODES, MAINNET_DISCOVERY_URL)); - verify(mockRunnerBuilder).p2pAdvertisedHost(eq("127.0.0.1")); - verify(mockRunnerBuilder).p2pListenPort(eq(30303)); verify(mockRunnerBuilder).jsonRpcConfiguration(eq(jsonRpcConfiguration)); verify(mockRunnerBuilder).graphQLConfiguration(eq(graphQLConfiguration)); verify(mockRunnerBuilder).webSocketConfiguration(eq(webSocketConfiguration)); diff --git a/besu/src/test/java/org/hyperledger/besu/cli/CommandTestAbstract.java b/besu/src/test/java/org/hyperledger/besu/cli/CommandTestAbstract.java index 8a6a5781198..2e40c385f80 100644 --- a/besu/src/test/java/org/hyperledger/besu/cli/CommandTestAbstract.java +++ b/besu/src/test/java/org/hyperledger/besu/cli/CommandTestAbstract.java @@ -19,9 +19,7 @@ import static org.hyperledger.besu.cli.util.CommandLineUtils.DEPENDENCY_WARNING_MSG; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; -import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.doReturn; @@ -67,6 +65,7 @@ import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool; import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; +import org.hyperledger.besu.ethereum.p2p.config.P2PConfiguration; import org.hyperledger.besu.ethereum.permissioning.PermissioningConfiguration; import org.hyperledger.besu.ethereum.storage.StorageProvider; import org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration; @@ -246,6 +245,7 @@ public abstract class CommandTestAbstract { @Captor protected ArgumentCaptor storageProviderArgumentCaptor; @Captor protected ArgumentCaptor ethProtocolConfigurationArgumentCaptor; @Captor protected ArgumentCaptor dataStorageConfigurationArgumentCaptor; + @Captor protected ArgumentCaptor p2PConfigurationArgumentCaptor; @Captor protected ArgumentCaptor> @@ -284,13 +284,9 @@ public void initMocks() throws Exception { when(mockControllerBuilder.reorgLoggingThreshold(anyLong())).thenReturn(mockControllerBuilder); when(mockControllerBuilder.dataStorageConfiguration(any())).thenReturn(mockControllerBuilder); when(mockControllerBuilder.evmConfiguration(any())).thenReturn(mockControllerBuilder); + when(mockControllerBuilder.p2PConfiguration(any())).thenReturn(mockControllerBuilder); when(mockControllerBuilder.networkConfiguration(any())).thenReturn(mockControllerBuilder); - when(mockControllerBuilder.randomPeerPriority(any())).thenReturn(mockControllerBuilder); - when(mockControllerBuilder.maxPeers(anyInt())).thenReturn(mockControllerBuilder); when(mockControllerBuilder.chainPruningConfiguration(any())).thenReturn(mockControllerBuilder); - when(mockControllerBuilder.maxPeers(anyInt())).thenReturn(mockControllerBuilder); - when(mockControllerBuilder.maxRemotelyInitiatedPeers(anyInt())) - .thenReturn(mockControllerBuilder); when(mockControllerBuilder.besuComponent(any(BesuComponent.class))) .thenReturn(mockControllerBuilder); when(mockControllerBuilder.cacheLastBlocks(any())).thenReturn(mockControllerBuilder); @@ -316,14 +312,9 @@ public void initMocks() throws Exception { when(mockRunnerBuilder.vertx(any())).thenReturn(mockRunnerBuilder); when(mockRunnerBuilder.besuController(any())).thenReturn(mockRunnerBuilder); - when(mockRunnerBuilder.discovery(anyBoolean())).thenReturn(mockRunnerBuilder); when(mockRunnerBuilder.ethNetworkConfig(any())).thenReturn(mockRunnerBuilder); 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.permissioningConfiguration(any())).thenReturn(mockRunnerBuilder); - when(mockRunnerBuilder.p2pEnabled(anyBoolean())).thenReturn(mockRunnerBuilder); when(mockRunnerBuilder.natMethod(any())).thenReturn(mockRunnerBuilder); when(mockRunnerBuilder.natManagerServiceName(any())).thenReturn(mockRunnerBuilder); when(mockRunnerBuilder.natMethodFallbackEnabled(anyBoolean())).thenReturn(mockRunnerBuilder); @@ -334,7 +325,6 @@ public void initMocks() throws Exception { when(mockRunnerBuilder.jsonRpcIpcConfiguration(any())).thenReturn(mockRunnerBuilder); when(mockRunnerBuilder.apiConfiguration(any())).thenReturn(mockRunnerBuilder); when(mockRunnerBuilder.dataDir(any())).thenReturn(mockRunnerBuilder); - when(mockRunnerBuilder.bannedNodeIds(any())).thenReturn(mockRunnerBuilder); when(mockRunnerBuilder.metricsSystem(any())).thenReturn(mockRunnerBuilder); when(mockRunnerBuilder.permissioningService(any())).thenReturn(mockRunnerBuilder); when(mockRunnerBuilder.metricsConfiguration(any())).thenReturn(mockRunnerBuilder); @@ -348,8 +338,8 @@ public void initMocks() throws Exception { when(mockRunnerBuilder.rpcEndpointService(any())).thenReturn(mockRunnerBuilder); when(mockRunnerBuilder.legacyForkId(anyBoolean())).thenReturn(mockRunnerBuilder); when(mockRunnerBuilder.apiConfiguration(any())).thenReturn(mockRunnerBuilder); + when(mockRunnerBuilder.p2PConfiguration(any())).thenReturn(mockRunnerBuilder); when(mockRunnerBuilder.enodeDnsConfiguration(any())).thenReturn(mockRunnerBuilder); - when(mockRunnerBuilder.allowedSubnets(any())).thenReturn(mockRunnerBuilder); when(mockRunnerBuilder.build()).thenReturn(mockRunner); final SignatureAlgorithm signatureAlgorithm = SignatureAlgorithmFactory.getInstance(); @@ -566,7 +556,7 @@ public static class TestBesuCommand extends BesuCommand { } @Override - protected void validateP2PInterface(final String p2pInterface) { + protected void validateP2PInterface() { // For testing, don't actually query for networking interfaces to validate this option } diff --git a/ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/config/AutoDiscoverDefaultIP.java b/ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/config/AutoDiscoverDefaultIP.java new file mode 100644 index 00000000000..59887997033 --- /dev/null +++ b/ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/config/AutoDiscoverDefaultIP.java @@ -0,0 +1,33 @@ +/* + * Copyright contributors to Hyperledger Besu. + * + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.ethereum.p2p.config; + +import java.net.InetAddress; +import java.util.Optional; + +public class AutoDiscoverDefaultIP { + private static InetAddress autoDiscoveredDefaultIP = null; + + // Used to discover the default IP of the client. + // Loopback IP is used by default as this is how smokeTests require it to be + // and it's probably a good security behaviour to default only on the localhost. + public static InetAddress autoDiscoverDefaultIP() { + + autoDiscoveredDefaultIP = + Optional.ofNullable(autoDiscoveredDefaultIP).orElseGet(InetAddress::getLoopbackAddress); + + return autoDiscoveredDefaultIP; + } +} diff --git a/ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/config/P2PConfiguration.java b/ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/config/P2PConfiguration.java new file mode 100644 index 00000000000..017ed7a5145 --- /dev/null +++ b/ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/config/P2PConfiguration.java @@ -0,0 +1,319 @@ +/* + * Copyright contributors to Hyperledger Besu. + * + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.ethereum.p2p.config; + +import static org.hyperledger.besu.ethereum.p2p.config.AutoDiscoverDefaultIP.autoDiscoverDefaultIP; +import static org.hyperledger.besu.ethereum.p2p.config.RlpxConfiguration.DEFAULT_FRACTION_REMOTE_CONNECTIONS_ALLOWED; + +import org.hyperledger.besu.ethereum.p2p.peers.EnodeURLImpl; +import org.hyperledger.besu.util.NetworkUtility; +import org.hyperledger.besu.util.number.Fraction; +import org.hyperledger.besu.util.number.Percentage; + +import java.util.List; +import java.util.Objects; + +import org.apache.commons.net.util.SubnetUtils.SubnetInfo; +import org.apache.tuweni.bytes.Bytes; + +public class P2PConfiguration { + // Public IP stored to prevent having to research it each time we need it. + private final boolean p2pEnabled; + private final boolean discoveryEnabled; + private final List bootNodes; + private final String host; + private final String p2pInterface; + private final int port; + private final int maxPeers; + private final boolean isLimitRemoteWireConnectionsEnabled; + private final Percentage maxRemoteConnectionsPercentage; + private final String discoveryDnsUrl; + private final boolean randomPeerPriority; + private final List bannedNodeIds; + private final List allowedSubnets; + + private P2PConfiguration( + final boolean p2pEnabled, + final boolean peerDiscoveryEnabled, + final List bootNodes, + final String host, + final String p2pInterface, + final int p2pPort, + final int maxPeers, + final boolean isLimitRemoteWireConnectionsEnabled, + final Percentage maxRemoteConnectionsPercentage, + final String discoveryDnsUrl, + final boolean randomPeerPriority, + final List bannedNodeIds, + final List allowedSubnets) { + this.p2pEnabled = p2pEnabled; + this.discoveryEnabled = peerDiscoveryEnabled; + this.bootNodes = bootNodes; + this.host = host; + this.p2pInterface = p2pInterface; + this.port = p2pPort; + this.maxPeers = maxPeers; + this.isLimitRemoteWireConnectionsEnabled = isLimitRemoteWireConnectionsEnabled; + this.maxRemoteConnectionsPercentage = maxRemoteConnectionsPercentage; + this.discoveryDnsUrl = discoveryDnsUrl; + this.randomPeerPriority = randomPeerPriority; + this.bannedNodeIds = bannedNodeIds; + this.allowedSubnets = allowedSubnets; + } + + public boolean isP2pEnabled() { + return p2pEnabled; + } + + public boolean isDiscoveryEnabled() { + return discoveryEnabled; + } + + public List getBootNodes() { + return bootNodes; + } + + public String getHost() { + return host; + } + + public String getP2pInterface() { + return p2pInterface; + } + + public int getPort() { + return port; + } + + public int getMaxPeers() { + return maxPeers; + } + + public String getDiscoveryDnsUrl() { + return discoveryDnsUrl; + } + + public boolean isRandomPeerPriority() { + return randomPeerPriority; + } + + public List getBannedNodeIds() { + return bannedNodeIds; + } + + public List getAllowedSubnets() { + return allowedSubnets; + } + + public int getMaxRemoteInitiatedPeers() { + if (isLimitRemoteWireConnectionsEnabled) { + final float fraction = Fraction.fromPercentage(maxRemoteConnectionsPercentage).getValue(); + return Math.round(fraction * maxPeers); + } + return maxPeers; + } + + public static P2PConfiguration createDefault() { + return new Builder() + .p2pEnabled(true) + .discoveryEnabled(true) + .host(autoDiscoverDefaultIP().getHostAddress()) + .p2pInterface(NetworkUtility.INADDR_ANY) + .port(EnodeURLImpl.DEFAULT_LISTENING_PORT) + .maxPeers(25) + .maxRemoteConnectionsPercentage( + Fraction.fromFloat(DEFAULT_FRACTION_REMOTE_CONNECTIONS_ALLOWED).toPercentage()) + .isLimitRemoteWireConnectionsEnabled(true) + .build(); + } + + public static P2PConfiguration.Builder builder() { + return new P2PConfiguration.Builder(); + } + + public static class Builder { + private boolean p2pEnabled = true; + private boolean discoveryEnabled = true; + private List bootNodes; + private String host; + private String p2pInterface = NetworkUtility.INADDR_ANY; + private int p2pPort = EnodeURLImpl.DEFAULT_LISTENING_PORT; + private int maxPeers; + private boolean isLimitRemoteWireConnectionsEnabled = true; + private Percentage maxRemoteConnectionsPercentage = + Fraction.fromFloat(DEFAULT_FRACTION_REMOTE_CONNECTIONS_ALLOWED).toPercentage(); + private String discoveryDnsUrl; + private boolean randomPeerPriority = false; + private List bannedNodeIds = List.of(); + private List allowedSubnets; + + public Builder p2pEnabled(final boolean p2pEnabled) { + this.p2pEnabled = p2pEnabled; + return this; + } + + public Builder discoveryEnabled(final boolean discoveryEnabled) { + this.discoveryEnabled = discoveryEnabled; + return this; + } + + public Builder bootNodes(final List bootNodes) { + this.bootNodes = bootNodes; + return this; + } + + public Builder host(final String host) { + this.host = host; + return this; + } + + public Builder p2pInterface(final String p2pInterface) { + this.p2pInterface = p2pInterface; + return this; + } + + public Builder port(final int listeningPort) { + this.p2pPort = listeningPort; + return this; + } + + public Builder maxPeers(final int maxPeers) { + this.maxPeers = maxPeers; + return this; + } + + public Builder isLimitRemoteWireConnectionsEnabled( + final boolean isLimitRemoteWireConnectionsEnabled) { + this.isLimitRemoteWireConnectionsEnabled = isLimitRemoteWireConnectionsEnabled; + return this; + } + + public Builder maxRemoteConnectionsPercentage(final Percentage maxRemoteConnectionsPercentage) { + this.maxRemoteConnectionsPercentage = maxRemoteConnectionsPercentage; + return this; + } + + public Builder discoveryDnsUrl(final String discoveryDnsUrl) { + this.discoveryDnsUrl = discoveryDnsUrl; + return this; + } + + public Builder randomPeerPriority(final boolean randomPeerPriority) { + this.randomPeerPriority = randomPeerPriority; + return this; + } + + public Builder bannedNodeIds(final List bannedNodeIds) { + this.bannedNodeIds = bannedNodeIds; + return this; + } + + public Builder allowedSubnets(final List allowedSubnets) { + this.allowedSubnets = allowedSubnets; + return this; + } + + public P2PConfiguration build() { + return new P2PConfiguration( + p2pEnabled, + discoveryEnabled, + bootNodes, + host, + p2pInterface, + p2pPort, + maxPeers, + isLimitRemoteWireConnectionsEnabled, + maxRemoteConnectionsPercentage, + discoveryDnsUrl, + randomPeerPriority, + bannedNodeIds, + allowedSubnets); + } + } + + @Override + public String toString() { + return "P2PConfiguration{" + + "p2pEnabled=" + + p2pEnabled + + ", discoveryEnabled=" + + discoveryEnabled + + ", bootNodes=" + + bootNodes + + ", host='" + + host + + '\'' + + ", p2pInterface='" + + p2pInterface + + '\'' + + ", port=" + + port + + ", maxPeers=" + + maxPeers + + ", isLimitRemoteWireConnectionsEnabled=" + + isLimitRemoteWireConnectionsEnabled + + ", maxRemoteConnectionsPercentage=" + + maxRemoteConnectionsPercentage + + ", discoveryDnsUrl='" + + discoveryDnsUrl + + '\'' + + ", randomPeerPriority=" + + randomPeerPriority + + ", bannedNodeIds=" + + bannedNodeIds + + ", allowedSubnets=" + + allowedSubnets + + '}'; + } + + @Override + public int hashCode() { + return Objects.hash( + p2pEnabled, + discoveryEnabled, + bootNodes, + host, + p2pInterface, + port, + maxPeers, + isLimitRemoteWireConnectionsEnabled, + maxRemoteConnectionsPercentage, + discoveryDnsUrl, + randomPeerPriority, + bannedNodeIds, + allowedSubnets); + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + P2PConfiguration that = (P2PConfiguration) o; + return p2pEnabled == that.p2pEnabled + && discoveryEnabled == that.discoveryEnabled + && port == that.port + && maxPeers == that.maxPeers + && isLimitRemoteWireConnectionsEnabled == that.isLimitRemoteWireConnectionsEnabled + && randomPeerPriority == that.randomPeerPriority + && Objects.equals(bootNodes, that.bootNodes) + && Objects.equals(host, that.host) + && Objects.equals(p2pInterface, that.p2pInterface) + && Objects.equals(maxRemoteConnectionsPercentage, that.maxRemoteConnectionsPercentage) + && Objects.equals(discoveryDnsUrl, that.discoveryDnsUrl) + && Objects.equals(bannedNodeIds, that.bannedNodeIds) + && Objects.equals(allowedSubnets, that.allowedSubnets); + } +}