Move connect decision into protocol layer (#4759)

Move the decision making for connecting or not connecting to peers into the eth layer. Future changes will take advantage if this to improve peering.

Signed-off-by: Stefan <stefan.pingel@consensys.net>

---------

Signed-off-by: Stefan <stefan.pingel@consensys.net>
Signed-off-by: Sally MacFarlane <macfarla.github@gmail.com>
Co-authored-by: Sally MacFarlane <macfarla.github@gmail.com>
pull/5344/head
Stefan Pingel 2 years ago committed by GitHub
parent 067a263374
commit 534a369574
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 9
      acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ThreadBesuNodeRunner.java
  2. 2
      acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeConfigurationBuilder.java
  3. 2
      acceptance-tests/test-plugins/src/main/java/org/hyperledger/besu/tests/acceptance/plugins/TestPermissioningPlugin.java
  4. 39
      acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/OpenTelemetryAcceptanceTest.java
  5. 118
      besu/src/main/java/org/hyperledger/besu/RunnerBuilder.java
  6. 72
      besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java
  7. 13
      besu/src/main/java/org/hyperledger/besu/cli/options/unstable/NetworkingOptions.java
  8. 12
      besu/src/main/java/org/hyperledger/besu/controller/BesuController.java
  9. 53
      besu/src/main/java/org/hyperledger/besu/controller/BesuControllerBuilder.java
  10. 2
      besu/src/main/java/org/hyperledger/besu/controller/TransitionBesuControllerBuilder.java
  11. 2
      besu/src/test/java/org/hyperledger/besu/PrivacyReorgTest.java
  12. 2
      besu/src/test/java/org/hyperledger/besu/PrivacyTest.java
  13. 5
      besu/src/test/java/org/hyperledger/besu/RunnerBuilderTest.java
  14. 7
      besu/src/test/java/org/hyperledger/besu/RunnerTest.java
  15. 2
      besu/src/test/java/org/hyperledger/besu/chainexport/RlpBlockExporterTest.java
  16. 2
      besu/src/test/java/org/hyperledger/besu/chainimport/JsonBlockImporterTest.java
  17. 4
      besu/src/test/java/org/hyperledger/besu/chainimport/RlpBlockImporterTest.java
  18. 64
      besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java
  19. 16
      besu/src/test/java/org/hyperledger/besu/cli/CommandTestAbstract.java
  20. 28
      besu/src/test/java/org/hyperledger/besu/cli/options/NetworkingOptionsTest.java
  21. 5
      besu/src/test/java/org/hyperledger/besu/controller/BesuControllerBuilderTest.java
  22. 25
      besu/src/test/java/org/hyperledger/besu/controller/MergeBesuControllerBuilderTest.java
  23. 4
      besu/src/test/java/org/hyperledger/besu/controller/QbftBesuControllerBuilderTest.java
  24. 6
      consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/protocol/BftProtocolManager.java
  25. 4
      consensus/common/src/test/java/org/hyperledger/besu/consensus/common/bft/EthSynchronizerUpdaterTest.java
  26. 4
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/execution/JsonRpcExecutor.java
  27. 9
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/AdminJsonRpcHttpServiceTest.java
  28. 3
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpServiceRpcApisTest.java
  29. 17
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/MockPeerConnection.java
  30. 3
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/AdminPeersTest.java
  31. 135
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/EthPeer.java
  32. 398
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/EthPeers.java
  33. 67
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/EthProtocolManager.java
  34. 6
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/snap/SnapProtocolManager.java
  35. 2
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/TrailingPeerLimiter.java
  36. 27
      ethereum/eth/src/test-support/java/org/hyperledger/besu/ethereum/eth/manager/MockPeerConnection.java
  37. 32
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/EthPeerTestUtil.java
  38. 62
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/EthPeerTest.java
  39. 18
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/EthPeersTest.java
  40. 64
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/EthProtocolManagerTestUtil.java
  41. 3
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/RequestManagerTest.java
  42. 6
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/RespondingEthPeer.java
  43. 10
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/ethtaskutils/AbstractMessageTaskTest.java
  44. 4
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/ethtaskutils/PeerMessageTaskTest.java
  45. 15
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/AbstractBlockPropagationManagerTest.java
  46. 2
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/TrailingPeerLimiterTest.java
  47. 23
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/TestNode.java
  48. 18
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPoolFactoryTest.java
  49. 31
      ethereum/mock-p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/testing/MockNetwork.java
  50. 4
      ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/config/DiscoveryConfiguration.java
  51. 12
      ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/config/NetworkingConfiguration.java
  52. 47
      ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/config/RlpxConfiguration.java
  53. 7
      ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/discovery/DiscoveryPeer.java
  54. 64
      ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/network/DefaultP2PNetwork.java
  55. 8
      ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/network/NetworkRunner.java
  56. 4
      ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/network/NoopP2PNetwork.java
  57. 12
      ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/network/P2PNetwork.java
  58. 9
      ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/network/ProtocolManager.java
  59. 13
      ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/peers/DefaultPeer.java
  60. 29
      ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/peers/Peer.java
  61. 488
      ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/rlpx/RlpxAgent.java
  62. 56
      ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/rlpx/connections/AbstractPeerConnection.java
  63. 16
      ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/rlpx/connections/PeerConnection.java
  64. 254
      ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/rlpx/connections/RlpxConnection.java
  65. 8
      ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/rlpx/connections/netty/AbstractHandshakeHandler.java
  66. 8
      ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/rlpx/connections/netty/DeFramer.java
  67. 3
      ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/rlpx/connections/netty/HandshakeHandlerInbound.java
  68. 3
      ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/rlpx/connections/netty/HandshakeHandlerOutbound.java
  69. 2
      ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/rlpx/connections/netty/NettyConnectionInitializer.java
  70. 6
      ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/rlpx/connections/netty/NettyPeerConnection.java
  71. 23
      ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/rlpx/wire/ShouldConnectCallback.java
  72. 66
      ethereum/p2p/src/test/java/org/hyperledger/besu/ethereum/p2p/config/RlpxConfigurationTest.java
  73. 26
      ethereum/p2p/src/test/java/org/hyperledger/besu/ethereum/p2p/network/DefaultP2PNetworkTest.java
  74. 10
      ethereum/p2p/src/test/java/org/hyperledger/besu/ethereum/p2p/network/NetworkingServiceLifecycleTest.java
  75. 77
      ethereum/p2p/src/test/java/org/hyperledger/besu/ethereum/p2p/network/P2PNetworkTest.java
  76. 40
      ethereum/p2p/src/test/java/org/hyperledger/besu/ethereum/p2p/network/P2PPlainNetworkTest.java
  77. 707
      ethereum/p2p/src/test/java/org/hyperledger/besu/ethereum/p2p/rlpx/RlpxAgentTest.java
  78. 3
      ethereum/p2p/src/test/java/org/hyperledger/besu/ethereum/p2p/rlpx/connections/AbstractPeerConnectionTest.java
  79. 10
      ethereum/p2p/src/test/java/org/hyperledger/besu/ethereum/p2p/rlpx/connections/MockConnectionInitializer.java
  80. 15
      ethereum/p2p/src/test/java/org/hyperledger/besu/ethereum/p2p/rlpx/connections/MockPeerConnection.java
  81. 269
      ethereum/p2p/src/test/java/org/hyperledger/besu/ethereum/p2p/rlpx/connections/RlpxConnectionTest.java
  82. 3
      ethereum/p2p/src/test/java/org/hyperledger/besu/ethereum/p2p/rlpx/connections/netty/DeFramerTest.java
  83. 2
      ethereum/permissioning/src/main/java/org/hyperledger/besu/ethereum/permissioning/node/InsufficientPeersPermissioningProvider.java
  84. 22
      ethereum/permissioning/src/test/java/org/hyperledger/besu/ethereum/permissioning/node/InsufficientPeersPermissioningProviderTest.java
  85. 13
      ethereum/retesteth/src/main/java/org/hyperledger/besu/ethereum/retesteth/RetestethContext.java
  86. 3
      util/src/main/java/org/hyperledger/besu/util/Subscribers.java

@ -168,7 +168,6 @@ public class ThreadBesuNodeRunner implements BesuNodeRunner {
.build(); .build();
final int maxPeers = 25; final int maxPeers = 25;
final int minPeers = 25;
builder builder
.synchronizerConfiguration(new SynchronizerConfiguration.Builder().build()) .synchronizerConfiguration(new SynchronizerConfiguration.Builder().build())
@ -187,7 +186,11 @@ public class ThreadBesuNodeRunner implements BesuNodeRunner {
node.getPkiKeyStoreConfiguration() node.getPkiKeyStoreConfiguration()
.map(pkiConfig -> new PkiBlockCreationConfigurationProvider().load(pkiConfig))) .map(pkiConfig -> new PkiBlockCreationConfigurationProvider().load(pkiConfig)))
.evmConfiguration(EvmConfiguration.DEFAULT) .evmConfiguration(EvmConfiguration.DEFAULT)
.maxPeers(maxPeers); .maxPeers(maxPeers)
.lowerBoundPeers(maxPeers)
.maxRemotelyInitiatedPeers(15)
.networkConfiguration(node.getNetworkingConfiguration())
.randomPeerPriority(false);
node.getGenesisConfig() node.getGenesisConfig()
.map(GenesisConfigFile::fromConfig) .map(GenesisConfigFile::fromConfig)
@ -205,8 +208,6 @@ public class ThreadBesuNodeRunner implements BesuNodeRunner {
.discovery(node.isDiscoveryEnabled()) .discovery(node.isDiscoveryEnabled())
.p2pAdvertisedHost(node.getHostName()) .p2pAdvertisedHost(node.getHostName())
.p2pListenPort(0) .p2pListenPort(0)
.maxPeers(maxPeers)
.minPeers(minPeers)
.networkingConfiguration(node.getNetworkingConfiguration()) .networkingConfiguration(node.getNetworkingConfiguration())
.jsonRpcConfiguration(node.jsonRpcConfiguration()) .jsonRpcConfiguration(node.jsonRpcConfiguration())
.webSocketConfiguration(node.webSocketConfiguration()) .webSocketConfiguration(node.webSocketConfiguration())

@ -412,7 +412,7 @@ public class BesuNodeConfigurationBuilder {
.withCrlPath(toPath(crl)); .withCrlPath(toPath(crl));
break; break;
} }
} catch (Exception e) { } catch (final Exception e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
this.tlsConfiguration = Optional.of(builder.build()); this.tlsConfiguration = Optional.of(builder.build());

@ -54,7 +54,7 @@ public class TestPermissioningPlugin implements BesuPlugin {
if (sourceEnode.toString().contains(bobNode) if (sourceEnode.toString().contains(bobNode)
|| destinationEnode.toString().contains(bobNode)) { || destinationEnode.toString().contains(bobNode)) {
boolean isBobTalkingToAlice = final boolean isBobTalkingToAlice =
sourceEnode.toString().contains(aliceNode) sourceEnode.toString().contains(aliceNode)
|| destinationEnode.toString().contains(aliceNode); || destinationEnode.toString().contains(aliceNode);
if (isBobTalkingToAlice) { if (isBobTalkingToAlice) {

@ -128,7 +128,7 @@ public class OpenTelemetryAcceptanceTest extends AcceptanceTestBase {
@BeforeEach @BeforeEach
public void setUp() throws Exception { public void setUp() throws Exception {
System.setProperty("root.log.level", "DEBUG"); System.setProperty("root.log.level", "DEBUG");
Server server = final Server server =
NettyServerBuilder.forPort(4317) NettyServerBuilder.forPort(4317)
.addService(fakeTracesCollector) .addService(fakeTracesCollector)
.addService(fakeMetricsCollector) .addService(fakeMetricsCollector)
@ -136,14 +136,14 @@ public class OpenTelemetryAcceptanceTest extends AcceptanceTestBase {
.start(); .start();
closer.register(server::shutdownNow); closer.register(server::shutdownNow);
MetricsConfiguration configuration = final MetricsConfiguration configuration =
MetricsConfiguration.builder() MetricsConfiguration.builder()
.protocol(MetricsProtocol.OPENTELEMETRY) .protocol(MetricsProtocol.OPENTELEMETRY)
.enabled(true) .enabled(true)
.port(0) .port(0)
.hostsAllowlist(singletonList("*")) .hostsAllowlist(singletonList("*"))
.build(); .build();
Map<String, String> env = new HashMap<>(); final Map<String, String> env = new HashMap<>();
env.put("OTEL_METRIC_EXPORT_INTERVAL", "1000"); env.put("OTEL_METRIC_EXPORT_INTERVAL", "1000");
env.put("OTEL_TRACES_SAMPLER", "always_on"); env.put("OTEL_TRACES_SAMPLER", "always_on");
env.put("OTEL_EXPORTER_OTLP_ENDPOINT", "http://localhost:4317"); env.put("OTEL_EXPORTER_OTLP_ENDPOINT", "http://localhost:4317");
@ -173,7 +173,7 @@ public class OpenTelemetryAcceptanceTest extends AcceptanceTestBase {
WaitUtils.waitFor( WaitUtils.waitFor(
30, 30,
() -> { () -> {
List<ResourceMetrics> resourceMetrics = fakeMetricsCollector.getReceivedMetrics(); final List<ResourceMetrics> resourceMetrics = fakeMetricsCollector.getReceivedMetrics();
assertThat(resourceMetrics.isEmpty()).isFalse(); assertThat(resourceMetrics.isEmpty()).isFalse();
}); });
} }
@ -186,26 +186,26 @@ public class OpenTelemetryAcceptanceTest extends AcceptanceTestBase {
() -> { () -> {
// call the json RPC endpoint to generate a trace. // call the json RPC endpoint to generate a trace.
net.netVersion().verify(metricsNode); net.netVersion().verify(metricsNode);
List<ResourceSpans> spans = fakeTracesCollector.getReceivedSpans(); final List<ResourceSpans> spans = fakeTracesCollector.getReceivedSpans();
assertThat(spans.isEmpty()).isFalse(); assertThat(spans.isEmpty()).isFalse();
Span internalSpan = spans.get(0).getScopeSpans(0).getSpans(0); final Span internalSpan = spans.get(0).getScopeSpans(0).getSpans(0);
assertThat(internalSpan.getKind()).isEqualTo(Span.SpanKind.SPAN_KIND_INTERNAL); assertThat(internalSpan.getKind()).isEqualTo(Span.SpanKind.SPAN_KIND_INTERNAL);
ByteString parent = internalSpan.getParentSpanId(); final ByteString parent = internalSpan.getParentSpanId();
assertThat(parent.isEmpty()).isFalse(); assertThat(parent.isEmpty()).isFalse();
Span serverSpan = spans.get(0).getScopeSpans(0).getSpans(1); final Span serverSpan = spans.get(0).getScopeSpans(0).getSpans(1);
assertThat(serverSpan.getKind()).isEqualTo(Span.SpanKind.SPAN_KIND_SERVER); assertThat(serverSpan.getKind()).isEqualTo(Span.SpanKind.SPAN_KIND_SERVER);
ByteString rootSpanId = serverSpan.getParentSpanId(); final ByteString rootSpanId = serverSpan.getParentSpanId();
assertThat(rootSpanId.isEmpty()).isTrue(); assertThat(rootSpanId.isEmpty()).isTrue();
}); });
} }
@Test @Test
public void traceReportingWithTraceId() { public void traceReportingWithTraceId() {
Duration timeout = Duration.ofSeconds(1); final Duration timeout = Duration.ofSeconds(1);
WaitUtils.waitFor( WaitUtils.waitFor(
60, 60,
() -> { () -> {
OpenTelemetry openTelemetry = final OpenTelemetry openTelemetry =
OpenTelemetrySdk.builder() OpenTelemetrySdk.builder()
.setPropagators( .setPropagators(
ContextPropagators.create( ContextPropagators.create(
@ -213,7 +213,7 @@ public class OpenTelemetryAcceptanceTest extends AcceptanceTestBase {
.setTracerProvider( .setTracerProvider(
SdkTracerProvider.builder().setSampler(Sampler.alwaysOn()).build()) SdkTracerProvider.builder().setSampler(Sampler.alwaysOn()).build())
.build(); .build();
Call.Factory client = final Call.Factory client =
OkHttpTelemetry.builder(openTelemetry) OkHttpTelemetry.builder(openTelemetry)
.build() .build()
.newCallFactory( .newCallFactory(
@ -222,7 +222,7 @@ public class OpenTelemetryAcceptanceTest extends AcceptanceTestBase {
.readTimeout(timeout) .readTimeout(timeout)
.writeTimeout(timeout) .writeTimeout(timeout)
.build()); .build());
Request request = final Request request =
new Request.Builder() new Request.Builder()
.url("http://localhost:" + metricsNode.getJsonRpcPort().get()) .url("http://localhost:" + metricsNode.getJsonRpcPort().get())
.post( .post(
@ -230,18 +230,19 @@ public class OpenTelemetryAcceptanceTest extends AcceptanceTestBase {
"{\"jsonrpc\":\"2.0\",\"method\":\"net_version\",\"params\":[],\"id\":255}", "{\"jsonrpc\":\"2.0\",\"method\":\"net_version\",\"params\":[],\"id\":255}",
MediaType.get("application/json"))) MediaType.get("application/json")))
.build(); .build();
Response response = client.newCall(request).execute(); final Response response = client.newCall(request).execute();
try { try {
assertThat(response.code()).isEqualTo(200); assertThat(response.code()).isEqualTo(200);
List<ResourceSpans> spans = new ArrayList<>(fakeTracesCollector.getReceivedSpans()); final List<ResourceSpans> spans =
new ArrayList<>(fakeTracesCollector.getReceivedSpans());
assertThat(spans.isEmpty()).isFalse(); assertThat(spans.isEmpty()).isFalse();
Span internalSpan = spans.get(0).getScopeSpans(0).getSpans(0); final Span internalSpan = spans.get(0).getScopeSpans(0).getSpans(0);
assertThat(internalSpan.getKind()).isEqualTo(Span.SpanKind.SPAN_KIND_INTERNAL); assertThat(internalSpan.getKind()).isEqualTo(Span.SpanKind.SPAN_KIND_INTERNAL);
ByteString parent = internalSpan.getParentSpanId(); final ByteString parent = internalSpan.getParentSpanId();
assertThat(parent.isEmpty()).isFalse(); assertThat(parent.isEmpty()).isFalse();
Span serverSpan = spans.get(0).getScopeSpans(0).getSpans(1); final Span serverSpan = spans.get(0).getScopeSpans(0).getSpans(1);
assertThat(serverSpan.getKind()).isEqualTo(Span.SpanKind.SPAN_KIND_SERVER); assertThat(serverSpan.getKind()).isEqualTo(Span.SpanKind.SPAN_KIND_SERVER);
ByteString rootSpanId = serverSpan.getParentSpanId(); final ByteString rootSpanId = serverSpan.getParentSpanId();
assertThat(rootSpanId.isEmpty()).isFalse(); assertThat(rootSpanId.isEmpty()).isFalse();
} finally { } finally {
response.close(); response.close();

@ -72,6 +72,7 @@ import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.MiningParameters; import org.hyperledger.besu.ethereum.core.MiningParameters;
import org.hyperledger.besu.ethereum.core.PrivacyParameters; import org.hyperledger.besu.ethereum.core.PrivacyParameters;
import org.hyperledger.besu.ethereum.core.Synchronizer; import org.hyperledger.besu.ethereum.core.Synchronizer;
import org.hyperledger.besu.ethereum.eth.manager.EthPeers;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool; import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.precompiles.privacy.FlexiblePrivacyPrecompiledContract; import org.hyperledger.besu.ethereum.mainnet.precompiles.privacy.FlexiblePrivacyPrecompiledContract;
@ -168,10 +169,6 @@ public class RunnerBuilder {
private NatMethod natMethod = NatMethod.AUTO; private NatMethod natMethod = NatMethod.AUTO;
private String natManagerServiceName; private String natManagerServiceName;
private boolean natMethodFallbackEnabled; private boolean natMethodFallbackEnabled;
private int maxPeers;
private int minPeers;
private boolean limitRemoteWireConnectionsEnabled = false;
private float fractionRemoteConnectionsAllowed;
private EthNetworkConfig ethNetworkConfig; private EthNetworkConfig ethNetworkConfig;
private EthstatsOptions ethstatsOptions; private EthstatsOptions ethstatsOptions;
private JsonRpcConfiguration jsonRpcConfiguration; private JsonRpcConfiguration jsonRpcConfiguration;
@ -189,7 +186,6 @@ public class RunnerBuilder {
private Optional<String> identityString = Optional.empty(); private Optional<String> identityString = Optional.empty();
private BesuPluginContextImpl besuPluginContext; private BesuPluginContextImpl besuPluginContext;
private boolean autoLogBloomCaching = true; private boolean autoLogBloomCaching = true;
private boolean randomPeerPriority;
private StorageProvider storageProvider; private StorageProvider storageProvider;
private RpcEndpointServiceImpl rpcEndpointServiceImpl; private RpcEndpointServiceImpl rpcEndpointServiceImpl;
private JsonRpcIpcConfiguration jsonRpcIpcConfiguration; private JsonRpcIpcConfiguration jsonRpcIpcConfiguration;
@ -354,52 +350,6 @@ public class RunnerBuilder {
return this; return this;
} }
/**
* Add Max peers.
*
* @param maxPeers the max peers
* @return the runner builder
*/
public RunnerBuilder maxPeers(final int maxPeers) {
this.maxPeers = maxPeers;
return this;
}
/**
* Enable Limit remote wire connections.
*
* @param limitRemoteWireConnectionsEnabled the limit remote wire connections enabled
* @return the runner builder
*/
public RunnerBuilder limitRemoteWireConnectionsEnabled(
final boolean limitRemoteWireConnectionsEnabled) {
this.limitRemoteWireConnectionsEnabled = limitRemoteWireConnectionsEnabled;
return this;
}
/**
* Add Fraction remote connections allowed.
*
* @param fractionRemoteConnectionsAllowed the fraction remote connections allowed
* @return the runner builder
*/
public RunnerBuilder fractionRemoteConnectionsAllowed(
final float fractionRemoteConnectionsAllowed) {
this.fractionRemoteConnectionsAllowed = fractionRemoteConnectionsAllowed;
return this;
}
/**
* Enable Random peer priority.
*
* @param randomPeerPriority the random peer priority
* @return the runner builder
*/
public RunnerBuilder randomPeerPriority(final boolean randomPeerPriority) {
this.randomPeerPriority = randomPeerPriority;
return this;
}
/** /**
* Add EthStatsOptions * Add EthStatsOptions
* *
@ -684,12 +634,8 @@ public class RunnerBuilder {
RlpxConfiguration.create() RlpxConfiguration.create()
.setBindHost(p2pListenInterface) .setBindHost(p2pListenInterface)
.setBindPort(p2pListenPort) .setBindPort(p2pListenPort)
.setPeerUpperBound(maxPeers)
.setPeerLowerBound(minPeers)
.setSupportedProtocols(subProtocols) .setSupportedProtocols(subProtocols)
.setClientId(BesuInfo.nodeName(identityString)) .setClientId(BesuInfo.nodeName(identityString));
.setLimitRemoteWireConnectionsEnabled(limitRemoteWireConnectionsEnabled)
.setFractionRemoteWireConnectionsAllowed(fractionRemoteConnectionsAllowed);
networkingConfiguration.setRlpx(rlpxConfiguration).setDiscovery(discoveryConfiguration); networkingConfiguration.setRlpx(rlpxConfiguration).setDiscovery(discoveryConfiguration);
final PeerPermissionsDenylist bannedNodes = PeerPermissionsDenylist.create(); final PeerPermissionsDenylist bannedNodes = PeerPermissionsDenylist.create();
@ -719,23 +665,27 @@ public class RunnerBuilder {
final NatService natService = new NatService(buildNatManager(natMethod), fallbackEnabled); final NatService natService = new NatService(buildNatManager(natMethod), fallbackEnabled);
final NetworkBuilder inactiveNetwork = caps -> new NoopP2PNetwork(); final NetworkBuilder inactiveNetwork = caps -> new NoopP2PNetwork();
final NetworkBuilder activeNetwork = final NetworkBuilder activeNetwork =
caps -> caps -> {
DefaultP2PNetwork.builder() final EthPeers ethPeers = besuController.getEthPeers();
.vertx(vertx) return DefaultP2PNetwork.builder()
.nodeKey(nodeKey) .vertx(vertx)
.config(networkingConfiguration) .nodeKey(nodeKey)
.legacyForkIdEnabled(legacyForkIdEnabled) .config(networkingConfiguration)
.peerPermissions(peerPermissions) .legacyForkIdEnabled(legacyForkIdEnabled)
.metricsSystem(metricsSystem) .peerPermissions(peerPermissions)
.supportedCapabilities(caps) .metricsSystem(metricsSystem)
.natService(natService) .supportedCapabilities(caps)
.randomPeerPriority(randomPeerPriority) .natService(natService)
.storageProvider(storageProvider) .storageProvider(storageProvider)
.p2pTLSConfiguration(p2pTLSConfiguration) .p2pTLSConfiguration(p2pTLSConfiguration)
.blockchain(context.getBlockchain()) .blockchain(context.getBlockchain())
.blockNumberForks(besuController.getGenesisConfigOptions().getForkBlockNumbers()) .blockNumberForks(besuController.getGenesisConfigOptions().getForkBlockNumbers())
.timestampForks(besuController.getGenesisConfigOptions().getForkBlockTimestamps()) .timestampForks(besuController.getGenesisConfigOptions().getForkBlockTimestamps())
.build(); .allConnectionsSupplier(ethPeers::getAllConnections)
.allActiveConnectionsSupplier(ethPeers::getAllActiveConnections)
.peersLowerBound(ethPeers.getPeerLowerBound())
.build();
};
final NetworkRunner networkRunner = final NetworkRunner networkRunner =
NetworkRunner.builder() NetworkRunner.builder()
@ -745,6 +695,8 @@ public class RunnerBuilder {
.metricsSystem(metricsSystem) .metricsSystem(metricsSystem)
.build(); .build();
besuController.getEthPeers().setRlpxAgent(networkRunner.getRlpxAgent());
final P2PNetwork network = networkRunner.getNetwork(); final P2PNetwork network = networkRunner.getNetwork();
// ForkId in Ethereum Node Record needs updating when we transition to a new protocol spec // ForkId in Ethereum Node Record needs updating when we transition to a new protocol spec
context context
@ -1423,26 +1375,6 @@ public class RunnerBuilder {
return MetricsService.create(vertx, configuration, metricsSystem); return MetricsService.create(vertx, configuration, metricsSystem);
} }
/**
* Gets minimum peers.
*
* @return the minimum peers
*/
public int getMinPeers() {
return minPeers;
}
/**
* Add Minimum peers.
*
* @param minPeers the minimum peers
* @return the runner builder
*/
public RunnerBuilder minPeers(final int minPeers) {
this.minPeers = minPeers;
return this;
}
/** /**
* Add Legacy fork id. * Add Legacy fork id.
* *

@ -15,6 +15,7 @@
package org.hyperledger.besu.cli; package org.hyperledger.besu.cli;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static java.nio.charset.StandardCharsets.UTF_8; import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Arrays.asList; import static java.util.Arrays.asList;
import static java.util.Collections.singletonList; import static java.util.Collections.singletonList;
@ -320,6 +321,10 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
private RocksDBPlugin rocksDBPlugin; private RocksDBPlugin rocksDBPlugin;
private int maxPeers;
private int maxRemoteInitiatedPeers;
private int peersLowerBound;
// CLI options defined by user at runtime. // CLI options defined by user at runtime.
// Options parsing is done with CLI library Picocli https://picocli.info/ // Options parsing is done with CLI library Picocli https://picocli.info/
@ -434,8 +439,6 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
description = "Maximum P2P connections that can be established (default: ${DEFAULT-VALUE})") description = "Maximum P2P connections that can be established (default: ${DEFAULT-VALUE})")
private final Integer maxPeers = DEFAULT_MAX_PEERS; private final Integer maxPeers = DEFAULT_MAX_PEERS;
private int minPeers;
@Option( @Option(
names = {"--remote-connections-limit-enabled"}, names = {"--remote-connections-limit-enabled"},
description = description =
@ -464,7 +467,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
names = {"--random-peer-priority-enabled"}, names = {"--random-peer-priority-enabled"},
description = description =
"Allow for incoming connections to be prioritized randomly. This will prevent (typically small, stable) networks from forming impenetrable peer cliques. (default: ${DEFAULT-VALUE})") "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 = false; private final Boolean randomPeerPriority = Boolean.FALSE;
@Option( @Option(
names = {"--banned-node-ids", "--banned-node-id"}, names = {"--banned-node-ids", "--banned-node-id"},
@ -1489,7 +1492,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
validateOptions(); validateOptions();
configure(); configure();
configureNativeLibs(); configureNativeLibs();
initController(); besuController = initController();
besuPluginContext.beforeExternalServices(); besuPluginContext.beforeExternalServices();
@ -1686,8 +1689,6 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
p2pTLSConfiguration, p2pTLSConfiguration,
p2PDiscoveryOptionGroup.peerDiscoveryEnabled, p2PDiscoveryOptionGroup.peerDiscoveryEnabled,
ethNetworkConfig, ethNetworkConfig,
p2PDiscoveryOptionGroup.maxPeers,
p2PDiscoveryOptionGroup.minPeers,
p2PDiscoveryOptionGroup.p2pHost, p2PDiscoveryOptionGroup.p2pHost,
p2PDiscoveryOptionGroup.p2pInterface, p2PDiscoveryOptionGroup.p2pInterface,
p2PDiscoveryOptionGroup.p2pPort, p2PDiscoveryOptionGroup.p2pPort,
@ -1974,17 +1975,29 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
} }
private void ensureValidPeerBoundParams() { private void ensureValidPeerBoundParams() {
final int min = unstableNetworkingOptions.toDomainObject().getRlpx().getPeerLowerBound(); maxPeers = p2PDiscoveryOptionGroup.maxPeers;
final int max = p2PDiscoveryOptionGroup.maxPeers; peersLowerBound = unstableNetworkingOptions.toDomainObject().getPeerLowerBound();
if (min > max) { if (peersLowerBound > maxPeers) {
logger.warn("`--Xp2p-peer-lower-bound` " + min + " must not exceed --max-peers " + max); logger.warn(
// modify the --X lower-bound value if it's not valid, we don't want unstable defaults "`--Xp2p-peer-lower-bound` "
// breaking things + peersLowerBound
logger.warn("setting --Xp2p-peer-lower-bound=" + max); + " must not exceed --max-peers "
unstableNetworkingOptions.toDomainObject().getRlpx().setPeerLowerBound(max); + maxPeers);
p2PDiscoveryOptionGroup.minPeers = max; logger.warn("setting --Xp2p-peer-lower-bound=" + maxPeers);
peersLowerBound = 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 = (int) Math.floor(fraction * maxPeers);
} else { } else {
p2PDiscoveryOptionGroup.minPeers = min; maxRemoteInitiatedPeers = maxPeers;
} }
} }
@ -1996,7 +2009,8 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
|| rpcEndpointServiceImpl.hasNamespace(apiName); || rpcEndpointServiceImpl.hasNamespace(apiName);
if (!jsonRPCHttpOptionGroup.rpcHttpApis.stream().allMatch(configuredApis)) { if (!jsonRPCHttpOptionGroup.rpcHttpApis.stream().allMatch(configuredApis)) {
List<String> invalidHttpApis = new ArrayList<String>(jsonRPCHttpOptionGroup.rpcHttpApis); final List<String> invalidHttpApis =
new ArrayList<String>(jsonRPCHttpOptionGroup.rpcHttpApis);
invalidHttpApis.removeAll(VALID_APIS); invalidHttpApis.removeAll(VALID_APIS);
throw new ParameterException( throw new ParameterException(
this.commandLine, this.commandLine,
@ -2005,12 +2019,12 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
} }
if (!jsonRPCWebsocketOptionGroup.rpcWsApis.stream().allMatch(configuredApis)) { if (!jsonRPCWebsocketOptionGroup.rpcWsApis.stream().allMatch(configuredApis)) {
List<String> invalidWsApis = new ArrayList<String>(jsonRPCWebsocketOptionGroup.rpcWsApis); final List<String> invalidWsApis =
new ArrayList<String>(jsonRPCWebsocketOptionGroup.rpcWsApis);
invalidWsApis.removeAll(VALID_APIS); invalidWsApis.removeAll(VALID_APIS);
throw new ParameterException( throw new ParameterException(
this.commandLine, this.commandLine,
"Invalid value for option '--rpc-ws-api': invalid entries found " "Invalid value for option '--rpc-ws-api': invalid entries found " + invalidWsApis);
+ invalidWsApis.toString());
} }
final boolean validHttpApiMethods = final boolean validHttpApiMethods =
@ -2212,8 +2226,8 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
} }
} }
private void initController() { private BesuController initController() {
besuController = buildController(); return buildController();
} }
/** /**
@ -2241,6 +2255,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
updateNetworkConfig(network), genesisConfigOverrides, getDefaultSyncModeIfNotSet()) updateNetworkConfig(network), genesisConfigOverrides, getDefaultSyncModeIfNotSet())
.synchronizerConfiguration(buildSyncConfig()) .synchronizerConfiguration(buildSyncConfig())
.ethProtocolConfiguration(unstableEthProtocolOptions.toDomainObject()) .ethProtocolConfiguration(unstableEthProtocolOptions.toDomainObject())
.networkConfiguration(unstableNetworkingOptions.toDomainObject())
.dataDirectory(dataDir()) .dataDirectory(dataDir())
.miningParameters( .miningParameters(
new MiningParameters.Builder() new MiningParameters.Builder()
@ -2284,6 +2299,9 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
.evmConfiguration(unstableEvmOptions.toDomainObject()) .evmConfiguration(unstableEvmOptions.toDomainObject())
.dataStorageConfiguration(dataStorageOptions.toDomainObject()) .dataStorageConfiguration(dataStorageOptions.toDomainObject())
.maxPeers(p2PDiscoveryOptionGroup.maxPeers) .maxPeers(p2PDiscoveryOptionGroup.maxPeers)
.lowerBoundPeers(peersLowerBound)
.maxRemotelyInitiatedPeers(maxRemoteInitiatedPeers)
.randomPeerPriority(p2PDiscoveryOptionGroup.randomPeerPriority)
.chainPruningConfiguration(unstableChainPruningOptions.toDomainObject()); .chainPruningConfiguration(unstableChainPruningOptions.toDomainObject());
} }
@ -2944,8 +2962,6 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
final Optional<TLSConfiguration> p2pTLSConfiguration, final Optional<TLSConfiguration> p2pTLSConfiguration,
final boolean peerDiscoveryEnabled, final boolean peerDiscoveryEnabled,
final EthNetworkConfig ethNetworkConfig, final EthNetworkConfig ethNetworkConfig,
final int maxPeers,
final int minPeers,
final String p2pAdvertisedHost, final String p2pAdvertisedHost,
final String p2pListenInterface, final String p2pListenInterface,
final int p2pListenPort, final int p2pListenPort,
@ -2979,14 +2995,6 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
.p2pAdvertisedHost(p2pAdvertisedHost) .p2pAdvertisedHost(p2pAdvertisedHost)
.p2pListenInterface(p2pListenInterface) .p2pListenInterface(p2pListenInterface)
.p2pListenPort(p2pListenPort) .p2pListenPort(p2pListenPort)
.maxPeers(maxPeers)
.minPeers(minPeers)
.limitRemoteWireConnectionsEnabled(
p2PDiscoveryOptionGroup.isLimitRemoteWireConnectionsEnabled)
.fractionRemoteConnectionsAllowed(
Fraction.fromPercentage(p2PDiscoveryOptionGroup.maxRemoteConnectionsPercentage)
.getValue())
.randomPeerPriority(p2PDiscoveryOptionGroup.randomPeerPriority)
.networkingConfiguration(unstableNetworkingOptions.toDomainObject()) .networkingConfiguration(unstableNetworkingOptions.toDomainObject())
.legacyForkId(unstableEthProtocolOptions.toDomainObject().isLegacyEth64ForkIdEnabled()) .legacyForkId(unstableEthProtocolOptions.toDomainObject().isLegacyEth64ForkIdEnabled())
.graphQLConfiguration(graphQLConfiguration) .graphQLConfiguration(graphQLConfiguration)

@ -27,13 +27,13 @@ import picocli.CommandLine;
/** The Networking Cli options. */ /** The Networking Cli options. */
public class NetworkingOptions implements CLIOptions<NetworkingConfiguration> { public class NetworkingOptions implements CLIOptions<NetworkingConfiguration> {
public static final String PEER_LOWER_BOUND_FLAG = "--Xp2p-peer-lower-bound";
private final String INITIATE_CONNECTIONS_FREQUENCY_FLAG = private final String INITIATE_CONNECTIONS_FREQUENCY_FLAG =
"--Xp2p-initiate-connections-frequency"; "--Xp2p-initiate-connections-frequency";
private final String CHECK_MAINTAINED_CONNECTIONS_FREQUENCY_FLAG = private final String CHECK_MAINTAINED_CONNECTIONS_FREQUENCY_FLAG =
"--Xp2p-check-maintained-connections-frequency"; "--Xp2p-check-maintained-connections-frequency";
private final String DNS_DISCOVERY_SERVER_OVERRIDE_FLAG = "--Xp2p-dns-discovery-server"; private final String DNS_DISCOVERY_SERVER_OVERRIDE_FLAG = "--Xp2p-dns-discovery-server";
private final String DISCOVERY_PROTOCOL_V5_ENABLED = "--Xv5-discovery-enabled"; private final String DISCOVERY_PROTOCOL_V5_ENABLED = "--Xv5-discovery-enabled";
private final String P2P_PEER_LOWER_BOUND_FLAG = "--Xp2p-peer-lower-bound";
/** The constant FILTER_ON_ENR_FORK_ID. */ /** The constant FILTER_ON_ENR_FORK_ID. */
public static final String FILTER_ON_ENR_FORK_ID = "--Xfilter-on-enr-fork-id"; public static final String FILTER_ON_ENR_FORK_ID = "--Xfilter-on-enr-fork-id";
@ -80,10 +80,10 @@ public class NetworkingOptions implements CLIOptions<NetworkingConfiguration> {
@CommandLine.Option( @CommandLine.Option(
hidden = true, hidden = true,
names = {P2P_PEER_LOWER_BOUND_FLAG}, names = PEER_LOWER_BOUND_FLAG,
description = description =
"Lower bound on the target number of P2P connections (default: ${DEFAULT-VALUE})") "Lower bound on the target number of P2P connections (default: ${DEFAULT-VALUE})")
private final Integer peerLowerBound = DefaultCommandValues.DEFAULT_P2P_PEER_LOWER_BOUND; private Integer peerLowerBoundConfig = DefaultCommandValues.DEFAULT_P2P_PEER_LOWER_BOUND;
private NetworkingOptions() {} private NetworkingOptions() {}
@ -109,6 +109,7 @@ public class NetworkingOptions implements CLIOptions<NetworkingConfiguration> {
cliOptions.initiateConnectionsFrequencySec = cliOptions.initiateConnectionsFrequencySec =
networkingConfig.getInitiateConnectionsFrequencySec(); networkingConfig.getInitiateConnectionsFrequencySec();
cliOptions.dnsDiscoveryServerOverride = networkingConfig.getDnsDiscoveryServerOverride(); cliOptions.dnsDiscoveryServerOverride = networkingConfig.getDnsDiscoveryServerOverride();
cliOptions.peerLowerBoundConfig = networkingConfig.getPeerLowerBound();
return cliOptions; return cliOptions;
} }
@ -121,7 +122,7 @@ public class NetworkingOptions implements CLIOptions<NetworkingConfiguration> {
config.setDnsDiscoveryServerOverride(dnsDiscoveryServerOverride); config.setDnsDiscoveryServerOverride(dnsDiscoveryServerOverride);
config.getDiscovery().setDiscoveryV5Enabled(isPeerDiscoveryV5Enabled); config.getDiscovery().setDiscoveryV5Enabled(isPeerDiscoveryV5Enabled);
config.getDiscovery().setFilterOnEnrForkId(filterOnEnrForkId); config.getDiscovery().setFilterOnEnrForkId(filterOnEnrForkId);
config.getRlpx().setPeerLowerBound(peerLowerBound); config.setPeerLowerBound(peerLowerBoundConfig);
return config; return config;
} }
@ -132,7 +133,9 @@ public class NetworkingOptions implements CLIOptions<NetworkingConfiguration> {
CHECK_MAINTAINED_CONNECTIONS_FREQUENCY_FLAG, CHECK_MAINTAINED_CONNECTIONS_FREQUENCY_FLAG,
OptionParser.format(checkMaintainedConnectionsFrequencySec), OptionParser.format(checkMaintainedConnectionsFrequencySec),
INITIATE_CONNECTIONS_FREQUENCY_FLAG, INITIATE_CONNECTIONS_FREQUENCY_FLAG,
OptionParser.format(initiateConnectionsFrequencySec)); OptionParser.format(initiateConnectionsFrequencySec),
PEER_LOWER_BOUND_FLAG,
OptionParser.format((peerLowerBoundConfig)));
if (dnsDiscoveryServerOverride.isPresent()) { if (dnsDiscoveryServerOverride.isPresent()) {
retval.add(DNS_DISCOVERY_SERVER_OVERRIDE_FLAG); retval.add(DNS_DISCOVERY_SERVER_OVERRIDE_FLAG);

@ -29,6 +29,7 @@ import org.hyperledger.besu.ethereum.blockcreation.MiningCoordinator;
import org.hyperledger.besu.ethereum.core.MiningParameters; import org.hyperledger.besu.ethereum.core.MiningParameters;
import org.hyperledger.besu.ethereum.core.PrivacyParameters; import org.hyperledger.besu.ethereum.core.PrivacyParameters;
import org.hyperledger.besu.ethereum.core.Synchronizer; import org.hyperledger.besu.ethereum.core.Synchronizer;
import org.hyperledger.besu.ethereum.eth.manager.EthPeers;
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager; import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager;
import org.hyperledger.besu.ethereum.eth.sync.SyncMode; import org.hyperledger.besu.ethereum.eth.sync.SyncMode;
import org.hyperledger.besu.ethereum.eth.sync.state.SyncState; import org.hyperledger.besu.ethereum.eth.sync.state.SyncState;
@ -73,6 +74,7 @@ public class BesuController implements java.io.Closeable {
private final MiningParameters miningParameters; private final MiningParameters miningParameters;
private final PluginServiceFactory additionalPluginServices; private final PluginServiceFactory additionalPluginServices;
private final SyncState syncState; private final SyncState syncState;
private final EthPeers ethPeers;
/** /**
* Instantiates a new Besu controller. * Instantiates a new Besu controller.
@ -108,7 +110,8 @@ public class BesuController implements java.io.Closeable {
final JsonRpcMethods additionalJsonRpcMethodsFactory, final JsonRpcMethods additionalJsonRpcMethodsFactory,
final NodeKey nodeKey, final NodeKey nodeKey,
final List<Closeable> closeables, final List<Closeable> closeables,
final PluginServiceFactory additionalPluginServices) { final PluginServiceFactory additionalPluginServices,
final EthPeers ethPeers) {
this.protocolSchedule = protocolSchedule; this.protocolSchedule = protocolSchedule;
this.protocolContext = protocolContext; this.protocolContext = protocolContext;
this.ethProtocolManager = ethProtocolManager; this.ethProtocolManager = ethProtocolManager;
@ -124,6 +127,7 @@ public class BesuController implements java.io.Closeable {
this.closeables = closeables; this.closeables = closeables;
this.miningParameters = miningParameters; this.miningParameters = miningParameters;
this.additionalPluginServices = additionalPluginServices; this.additionalPluginServices = additionalPluginServices;
this.ethPeers = ethPeers;
} }
/** /**
@ -207,6 +211,10 @@ public class BesuController implements java.io.Closeable {
return miningCoordinator; return miningCoordinator;
} }
public EthPeers getEthPeers() {
return ethPeers;
}
@Override @Override
public void close() { public void close() {
closeables.forEach(this::tryClose); closeables.forEach(this::tryClose);
@ -215,7 +223,7 @@ public class BesuController implements java.io.Closeable {
private void tryClose(final Closeable closeable) { private void tryClose(final Closeable closeable) {
try { try {
closeable.close(); closeable.close();
} catch (IOException e) { } catch (final IOException e) {
LOG.error("Unable to close resource.", e); LOG.error("Unable to close resource.", e);
} }
} }

@ -76,6 +76,7 @@ import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfigurati
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolFactory; import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolFactory;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec; import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec;
import org.hyperledger.besu.ethereum.p2p.config.NetworkingConfiguration;
import org.hyperledger.besu.ethereum.p2p.config.SubProtocolConfiguration; import org.hyperledger.besu.ethereum.p2p.config.SubProtocolConfiguration;
import org.hyperledger.besu.ethereum.storage.StorageProvider; import org.hyperledger.besu.ethereum.storage.StorageProvider;
import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueSegmentIdentifier; import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueSegmentIdentifier;
@ -169,9 +170,15 @@ public abstract class BesuControllerBuilder implements MiningParameterOverrides
protected EvmConfiguration evmConfiguration; protected EvmConfiguration evmConfiguration;
/** The Max peers. */ /** The Max peers. */
protected int maxPeers; protected int maxPeers;
private int peerLowerBound;
private int maxRemotelyInitiatedPeers;
/** The Chain pruner configuration. */ /** The Chain pruner configuration. */
protected ChainPrunerConfiguration chainPrunerConfiguration = ChainPrunerConfiguration.DEFAULT; protected ChainPrunerConfiguration chainPrunerConfiguration = ChainPrunerConfiguration.DEFAULT;
private NetworkingConfiguration networkingConfiguration;
private Boolean randomPeerPriority;
/** /**
* Storage provider besu controller builder. * Storage provider besu controller builder.
* *
@ -443,6 +450,29 @@ public abstract class BesuControllerBuilder implements MiningParameterOverrides
return this; return this;
} }
/**
* Lower bound of peers where we stop actively trying to initiate new outgoing connections
*
* @param peerLowerBound lower bound of peers where we stop actively trying to initiate new
* outgoing connections
* @return the besu controller builder
*/
public BesuControllerBuilder lowerBoundPeers(final int peerLowerBound) {
this.peerLowerBound = peerLowerBound;
return this;
}
/**
* Maximum number of remotely initiated peer connections
*
* @param maxRemotelyInitiatedPeers aximum number of remotely initiated peer connections
* @return the besu controller builder
*/
public BesuControllerBuilder maxRemotelyInitiatedPeers(final int maxRemotelyInitiatedPeers) {
this.maxRemotelyInitiatedPeers = maxRemotelyInitiatedPeers;
return this;
}
/** /**
* Chain pruning configuration besu controller builder. * Chain pruning configuration besu controller builder.
* *
@ -455,6 +485,17 @@ public abstract class BesuControllerBuilder implements MiningParameterOverrides
return this; return this;
} }
public BesuControllerBuilder networkConfiguration(
final NetworkingConfiguration networkingConfiguration) {
this.networkingConfiguration = networkingConfiguration;
return this;
}
public BesuControllerBuilder randomPeerPriority(final Boolean randomPeerPriority) {
this.randomPeerPriority = randomPeerPriority;
return this;
}
/** /**
* Build besu controller. * Build besu controller.
* *
@ -475,6 +516,7 @@ public abstract class BesuControllerBuilder implements MiningParameterOverrides
checkNotNull(storageProvider, "Must supply a storage provider"); checkNotNull(storageProvider, "Must supply a storage provider");
checkNotNull(gasLimitCalculator, "Missing gas limit calculator"); checkNotNull(gasLimitCalculator, "Missing gas limit calculator");
checkNotNull(evmConfiguration, "Missing evm config"); checkNotNull(evmConfiguration, "Missing evm config");
checkNotNull(networkingConfiguration, "Missing network configuration");
prepForBuild(); prepForBuild();
final ProtocolSchedule protocolSchedule = createProtocolSchedule(); final ProtocolSchedule protocolSchedule = createProtocolSchedule();
@ -557,9 +599,13 @@ public abstract class BesuControllerBuilder implements MiningParameterOverrides
currentProtocolSpecSupplier, currentProtocolSpecSupplier,
clock, clock,
metricsSystem, metricsSystem,
maxPeers,
maxMessageSize, maxMessageSize,
messagePermissioningProviders); messagePermissioningProviders,
nodeKey.getPublicKey().getEncodedBytes(),
peerLowerBound,
maxPeers,
maxRemotelyInitiatedPeers,
randomPeerPriority);
final EthMessages ethMessages = new EthMessages(); final EthMessages ethMessages = new EthMessages();
final EthMessages snapMessages = new EthMessages(); final EthMessages snapMessages = new EthMessages();
@ -679,7 +725,8 @@ public abstract class BesuControllerBuilder implements MiningParameterOverrides
additionalJsonRpcMethodFactory, additionalJsonRpcMethodFactory,
nodeKey, nodeKey,
closeables, closeables,
additionalPluginServices); additionalPluginServices,
ethPeers);
} }
/** /**

@ -287,7 +287,7 @@ public class TransitionBesuControllerBuilder extends BesuControllerBuilder {
@Override @Override
public BesuController build() { public BesuController build() {
BesuController controller = super.build(); final BesuController controller = super.build();
PostMergeContext.get().setSyncState(controller.getSyncState()); PostMergeContext.get().setSyncState(controller.getSyncState());
return controller; return controller;
} }

@ -48,6 +48,7 @@ import org.hyperledger.besu.ethereum.eth.sync.SyncMode;
import org.hyperledger.besu.ethereum.eth.sync.SynchronizerConfiguration; import org.hyperledger.besu.ethereum.eth.sync.SynchronizerConfiguration;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration; import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration;
import org.hyperledger.besu.ethereum.mainnet.HeaderValidationMode; import org.hyperledger.besu.ethereum.mainnet.HeaderValidationMode;
import org.hyperledger.besu.ethereum.p2p.config.NetworkingConfiguration;
import org.hyperledger.besu.ethereum.privacy.PrivateStateRootResolver; import org.hyperledger.besu.ethereum.privacy.PrivateStateRootResolver;
import org.hyperledger.besu.ethereum.privacy.PrivateTransaction; import org.hyperledger.besu.ethereum.privacy.PrivateTransaction;
import org.hyperledger.besu.ethereum.privacy.storage.PrivacyGroupHeadBlockMap; import org.hyperledger.besu.ethereum.privacy.storage.PrivacyGroupHeadBlockMap;
@ -199,6 +200,7 @@ public class PrivacyReorgTest {
.transactionPoolConfiguration(TransactionPoolConfiguration.DEFAULT) .transactionPoolConfiguration(TransactionPoolConfiguration.DEFAULT)
.gasLimitCalculator(GasLimitCalculator.constant()) .gasLimitCalculator(GasLimitCalculator.constant())
.evmConfiguration(EvmConfiguration.DEFAULT) .evmConfiguration(EvmConfiguration.DEFAULT)
.networkConfiguration(NetworkingConfiguration.create())
.build(); .build();
} }

@ -38,6 +38,7 @@ import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration;
import org.hyperledger.besu.ethereum.eth.sync.SyncMode; import org.hyperledger.besu.ethereum.eth.sync.SyncMode;
import org.hyperledger.besu.ethereum.eth.sync.SynchronizerConfiguration; import org.hyperledger.besu.ethereum.eth.sync.SynchronizerConfiguration;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration; import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration;
import org.hyperledger.besu.ethereum.p2p.config.NetworkingConfiguration;
import org.hyperledger.besu.ethereum.privacy.storage.PrivacyStorageProvider; import org.hyperledger.besu.ethereum.privacy.storage.PrivacyStorageProvider;
import org.hyperledger.besu.ethereum.privacy.storage.keyvalue.PrivacyKeyValueStorageProviderBuilder; import org.hyperledger.besu.ethereum.privacy.storage.keyvalue.PrivacyKeyValueStorageProviderBuilder;
import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueSegmentIdentifier; import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueSegmentIdentifier;
@ -120,6 +121,7 @@ public class PrivacyTest {
.transactionPoolConfiguration(TransactionPoolConfiguration.DEFAULT) .transactionPoolConfiguration(TransactionPoolConfiguration.DEFAULT)
.gasLimitCalculator(GasLimitCalculator.constant()) .gasLimitCalculator(GasLimitCalculator.constant())
.evmConfiguration(EvmConfiguration.DEFAULT) .evmConfiguration(EvmConfiguration.DEFAULT)
.networkConfiguration(NetworkingConfiguration.create())
.build(); .build();
} }

@ -55,11 +55,13 @@ import org.hyperledger.besu.ethereum.core.MiningParameters;
import org.hyperledger.besu.ethereum.core.PrivacyParameters; import org.hyperledger.besu.ethereum.core.PrivacyParameters;
import org.hyperledger.besu.ethereum.core.Synchronizer; import org.hyperledger.besu.ethereum.core.Synchronizer;
import org.hyperledger.besu.ethereum.eth.manager.EthContext; import org.hyperledger.besu.ethereum.eth.manager.EthContext;
import org.hyperledger.besu.ethereum.eth.manager.EthPeers;
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager; import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager;
import org.hyperledger.besu.ethereum.eth.manager.EthScheduler; import org.hyperledger.besu.ethereum.eth.manager.EthScheduler;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool; import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
import org.hyperledger.besu.ethereum.mainnet.MainnetBlockHeaderFunctions; import org.hyperledger.besu.ethereum.mainnet.MainnetBlockHeaderFunctions;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.p2p.config.NetworkingConfiguration;
import org.hyperledger.besu.ethereum.p2p.config.SubProtocolConfiguration; import org.hyperledger.besu.ethereum.p2p.config.SubProtocolConfiguration;
import org.hyperledger.besu.ethereum.p2p.peers.EnodeURLImpl; import org.hyperledger.besu.ethereum.p2p.peers.EnodeURLImpl;
import org.hyperledger.besu.ethereum.storage.StorageProvider; import org.hyperledger.besu.ethereum.storage.StorageProvider;
@ -138,6 +140,7 @@ public final class RunnerBuilderTest {
when(besuController.getSynchronizer()).thenReturn(mock(Synchronizer.class)); when(besuController.getSynchronizer()).thenReturn(mock(Synchronizer.class));
when(besuController.getMiningCoordinator()).thenReturn(mock(MiningCoordinator.class)); when(besuController.getMiningCoordinator()).thenReturn(mock(MiningCoordinator.class));
when(besuController.getMiningCoordinator()).thenReturn(mock(MergeMiningCoordinator.class)); when(besuController.getMiningCoordinator()).thenReturn(mock(MergeMiningCoordinator.class));
when(besuController.getEthPeers()).thenReturn(mock(EthPeers.class));
final GenesisConfigOptions genesisConfigOptions = mock(GenesisConfigOptions.class); final GenesisConfigOptions genesisConfigOptions = mock(GenesisConfigOptions.class);
when(genesisConfigOptions.getForkBlockNumbers()).thenReturn(Collections.emptyList()); when(genesisConfigOptions.getForkBlockNumbers()).thenReturn(Collections.emptyList());
when(genesisConfigOptions.getForkBlockTimestamps()).thenReturn(Collections.emptyList()); when(genesisConfigOptions.getForkBlockTimestamps()).thenReturn(Collections.emptyList());
@ -389,6 +392,7 @@ public final class RunnerBuilderTest {
.storageProvider(mock(KeyValueStorageProvider.class)) .storageProvider(mock(KeyValueStorageProvider.class))
.rpcEndpointService(new RpcEndpointServiceImpl()) .rpcEndpointService(new RpcEndpointServiceImpl())
.besuPluginContext(mock(BesuPluginContextImpl.class)) .besuPluginContext(mock(BesuPluginContextImpl.class))
.networkingConfiguration(NetworkingConfiguration.create())
.build(); .build();
assertThat(runner.getJsonRpcPort()).isPresent(); assertThat(runner.getJsonRpcPort()).isPresent();
@ -435,6 +439,7 @@ public final class RunnerBuilderTest {
.storageProvider(mock(KeyValueStorageProvider.class)) .storageProvider(mock(KeyValueStorageProvider.class))
.rpcEndpointService(new RpcEndpointServiceImpl()) .rpcEndpointService(new RpcEndpointServiceImpl())
.besuPluginContext(mock(BesuPluginContextImpl.class)) .besuPluginContext(mock(BesuPluginContextImpl.class))
.networkingConfiguration(NetworkingConfiguration.create())
.build(); .build();
verify(mockTransitionCoordinator, times(1)).getPreMergeObject(); verify(mockTransitionCoordinator, times(1)).getPreMergeObject();

@ -52,6 +52,7 @@ import org.hyperledger.besu.ethereum.mainnet.BlockImportResult;
import org.hyperledger.besu.ethereum.mainnet.HeaderValidationMode; import org.hyperledger.besu.ethereum.mainnet.HeaderValidationMode;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec; import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec;
import org.hyperledger.besu.ethereum.p2p.config.NetworkingConfiguration;
import org.hyperledger.besu.ethereum.p2p.peers.EnodeURLImpl; import org.hyperledger.besu.ethereum.p2p.peers.EnodeURLImpl;
import org.hyperledger.besu.ethereum.storage.StorageProvider; import org.hyperledger.besu.ethereum.storage.StorageProvider;
import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueSegmentIdentifier; import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueSegmentIdentifier;
@ -188,7 +189,6 @@ public final class RunnerTest {
.discovery(true) .discovery(true)
.p2pAdvertisedHost(listenHost) .p2pAdvertisedHost(listenHost)
.p2pListenPort(0) .p2pListenPort(0)
.maxPeers(3)
.metricsSystem(noOpMetricsSystem) .metricsSystem(noOpMetricsSystem)
.permissioningService(new PermissioningServiceImpl()) .permissioningService(new PermissioningServiceImpl())
.staticNodes(emptySet()) .staticNodes(emptySet())
@ -459,6 +459,11 @@ public final class RunnerTest {
.transactionPoolConfiguration(TransactionPoolConfiguration.DEFAULT) .transactionPoolConfiguration(TransactionPoolConfiguration.DEFAULT)
.gasLimitCalculator(GasLimitCalculator.constant()) .gasLimitCalculator(GasLimitCalculator.constant())
.evmConfiguration(EvmConfiguration.DEFAULT) .evmConfiguration(EvmConfiguration.DEFAULT)
.networkConfiguration(NetworkingConfiguration.create())
.randomPeerPriority(Boolean.FALSE)
.maxPeers(25)
.lowerBoundPeers(25)
.maxRemotelyInitiatedPeers(15)
.build(); .build();
} }
} }

@ -35,6 +35,7 @@ import org.hyperledger.besu.ethereum.eth.sync.SynchronizerConfiguration;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration; import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.ScheduleBasedBlockHeaderFunctions; import org.hyperledger.besu.ethereum.mainnet.ScheduleBasedBlockHeaderFunctions;
import org.hyperledger.besu.ethereum.p2p.config.NetworkingConfiguration;
import org.hyperledger.besu.ethereum.util.RawBlockIterator; import org.hyperledger.besu.ethereum.util.RawBlockIterator;
import org.hyperledger.besu.evm.internal.EvmConfiguration; import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
@ -98,6 +99,7 @@ public final class RlpBlockExporterTest {
.transactionPoolConfiguration(TransactionPoolConfiguration.DEFAULT) .transactionPoolConfiguration(TransactionPoolConfiguration.DEFAULT)
.gasLimitCalculator(GasLimitCalculator.constant()) .gasLimitCalculator(GasLimitCalculator.constant())
.evmConfiguration(EvmConfiguration.DEFAULT) .evmConfiguration(EvmConfiguration.DEFAULT)
.networkConfiguration(NetworkingConfiguration.create())
.build(); .build();
} }

@ -37,6 +37,7 @@ import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration;
import org.hyperledger.besu.ethereum.eth.sync.SyncMode; import org.hyperledger.besu.ethereum.eth.sync.SyncMode;
import org.hyperledger.besu.ethereum.eth.sync.SynchronizerConfiguration; import org.hyperledger.besu.ethereum.eth.sync.SynchronizerConfiguration;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration; import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration;
import org.hyperledger.besu.ethereum.p2p.config.NetworkingConfiguration;
import org.hyperledger.besu.evm.internal.EvmConfiguration; import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
import org.hyperledger.besu.testutil.TestClock; import org.hyperledger.besu.testutil.TestClock;
@ -432,6 +433,7 @@ public abstract class JsonBlockImporterTest {
.transactionPoolConfiguration(TransactionPoolConfiguration.DEFAULT) .transactionPoolConfiguration(TransactionPoolConfiguration.DEFAULT)
.gasLimitCalculator(GasLimitCalculator.constant()) .gasLimitCalculator(GasLimitCalculator.constant())
.evmConfiguration(EvmConfiguration.DEFAULT) .evmConfiguration(EvmConfiguration.DEFAULT)
.networkConfiguration(NetworkingConfiguration.create())
.build(); .build();
} }
} }

@ -29,6 +29,7 @@ import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration;
import org.hyperledger.besu.ethereum.eth.sync.SyncMode; import org.hyperledger.besu.ethereum.eth.sync.SyncMode;
import org.hyperledger.besu.ethereum.eth.sync.SynchronizerConfiguration; import org.hyperledger.besu.ethereum.eth.sync.SynchronizerConfiguration;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration; import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration;
import org.hyperledger.besu.ethereum.p2p.config.NetworkingConfiguration;
import org.hyperledger.besu.evm.internal.EvmConfiguration; import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
import org.hyperledger.besu.testutil.BlockTestUtil; import org.hyperledger.besu.testutil.BlockTestUtil;
@ -75,6 +76,7 @@ public final class RlpBlockImporterTest {
.transactionPoolConfiguration(TransactionPoolConfiguration.DEFAULT) .transactionPoolConfiguration(TransactionPoolConfiguration.DEFAULT)
.gasLimitCalculator(GasLimitCalculator.constant()) .gasLimitCalculator(GasLimitCalculator.constant())
.evmConfiguration(EvmConfiguration.DEFAULT) .evmConfiguration(EvmConfiguration.DEFAULT)
.networkConfiguration(NetworkingConfiguration.create())
.build(); .build();
final RlpBlockImporter.ImportResult result = final RlpBlockImporter.ImportResult result =
rlpBlockImporter.importBlockchain(source, targetController, false); rlpBlockImporter.importBlockchain(source, targetController, false);
@ -107,6 +109,7 @@ public final class RlpBlockImporterTest {
.transactionPoolConfiguration(TransactionPoolConfiguration.DEFAULT) .transactionPoolConfiguration(TransactionPoolConfiguration.DEFAULT)
.gasLimitCalculator(GasLimitCalculator.constant()) .gasLimitCalculator(GasLimitCalculator.constant())
.evmConfiguration(EvmConfiguration.DEFAULT) .evmConfiguration(EvmConfiguration.DEFAULT)
.networkConfiguration(NetworkingConfiguration.create())
.build(); .build();
assertThatThrownBy( assertThatThrownBy(
@ -136,6 +139,7 @@ public final class RlpBlockImporterTest {
.transactionPoolConfiguration(TransactionPoolConfiguration.DEFAULT) .transactionPoolConfiguration(TransactionPoolConfiguration.DEFAULT)
.gasLimitCalculator(GasLimitCalculator.constant()) .gasLimitCalculator(GasLimitCalculator.constant())
.evmConfiguration(EvmConfiguration.DEFAULT) .evmConfiguration(EvmConfiguration.DEFAULT)
.networkConfiguration(NetworkingConfiguration.create())
.build(); .build();
final RlpBlockImporter.ImportResult result = final RlpBlockImporter.ImportResult result =

@ -249,8 +249,6 @@ public class BesuCommandTest extends CommandTestAbstract {
MAINNET_DISCOVERY_URL)); MAINNET_DISCOVERY_URL));
verify(mockRunnerBuilder).p2pAdvertisedHost(eq("127.0.0.1")); verify(mockRunnerBuilder).p2pAdvertisedHost(eq("127.0.0.1"));
verify(mockRunnerBuilder).p2pListenPort(eq(30303)); verify(mockRunnerBuilder).p2pListenPort(eq(30303));
verify(mockRunnerBuilder).maxPeers(eq(maxPeers));
verify(mockRunnerBuilder).fractionRemoteConnectionsAllowed(eq(0.6f));
verify(mockRunnerBuilder).jsonRpcConfiguration(eq(DEFAULT_JSON_RPC_CONFIGURATION)); verify(mockRunnerBuilder).jsonRpcConfiguration(eq(DEFAULT_JSON_RPC_CONFIGURATION));
verify(mockRunnerBuilder).graphQLConfiguration(eq(DEFAULT_GRAPH_QL_CONFIGURATION)); verify(mockRunnerBuilder).graphQLConfiguration(eq(DEFAULT_GRAPH_QL_CONFIGURATION));
verify(mockRunnerBuilder).webSocketConfiguration(eq(DEFAULT_WEB_SOCKET_CONFIGURATION)); verify(mockRunnerBuilder).webSocketConfiguration(eq(DEFAULT_WEB_SOCKET_CONFIGURATION));
@ -271,6 +269,8 @@ public class BesuCommandTest extends CommandTestAbstract {
verify(mockControllerBuilder).storageProvider(storageProviderArgumentCaptor.capture()); verify(mockControllerBuilder).storageProvider(storageProviderArgumentCaptor.capture());
verify(mockControllerBuilder).gasLimitCalculator(eq(GasLimitCalculator.constant())); verify(mockControllerBuilder).gasLimitCalculator(eq(GasLimitCalculator.constant()));
verify(mockControllerBuilder).maxPeers(eq(maxPeers)); verify(mockControllerBuilder).maxPeers(eq(maxPeers));
verify(mockControllerBuilder).lowerBoundPeers(eq(maxPeers));
verify(mockControllerBuilder).maxRemotelyInitiatedPeers(eq((int) Math.floor(0.6 * maxPeers)));
verify(mockControllerBuilder).build(); verify(mockControllerBuilder).build();
assertThat(storageProviderArgumentCaptor.getValue()).isNotNull(); assertThat(storageProviderArgumentCaptor.getValue()).isNotNull();
@ -401,7 +401,6 @@ public class BesuCommandTest extends CommandTestAbstract {
verify(mockRunnerBuilder).ethNetworkConfig(ethNetworkConfigArgumentCaptor.capture()); verify(mockRunnerBuilder).ethNetworkConfig(ethNetworkConfigArgumentCaptor.capture());
verify(mockRunnerBuilder).p2pAdvertisedHost(eq("1.2.3.4")); verify(mockRunnerBuilder).p2pAdvertisedHost(eq("1.2.3.4"));
verify(mockRunnerBuilder).p2pListenPort(eq(1234)); verify(mockRunnerBuilder).p2pListenPort(eq(1234));
verify(mockRunnerBuilder).maxPeers(eq(42));
verify(mockRunnerBuilder).jsonRpcConfiguration(eq(jsonRpcConfiguration)); verify(mockRunnerBuilder).jsonRpcConfiguration(eq(jsonRpcConfiguration));
verify(mockRunnerBuilder).graphQLConfiguration(eq(graphQLConfiguration)); verify(mockRunnerBuilder).graphQLConfiguration(eq(graphQLConfiguration));
verify(mockRunnerBuilder).webSocketConfiguration(eq(webSocketConfiguration)); verify(mockRunnerBuilder).webSocketConfiguration(eq(webSocketConfiguration));
@ -876,9 +875,6 @@ public class BesuCommandTest extends CommandTestAbstract {
MAINNET_DISCOVERY_URL)); MAINNET_DISCOVERY_URL));
verify(mockRunnerBuilder).p2pAdvertisedHost(eq("127.0.0.1")); verify(mockRunnerBuilder).p2pAdvertisedHost(eq("127.0.0.1"));
verify(mockRunnerBuilder).p2pListenPort(eq(30303)); verify(mockRunnerBuilder).p2pListenPort(eq(30303));
verify(mockRunnerBuilder).maxPeers(eq(25));
verify(mockRunnerBuilder).limitRemoteWireConnectionsEnabled(eq(true));
verify(mockRunnerBuilder).fractionRemoteConnectionsAllowed(eq(0.6f));
verify(mockRunnerBuilder).jsonRpcConfiguration(eq(jsonRpcConfiguration)); verify(mockRunnerBuilder).jsonRpcConfiguration(eq(jsonRpcConfiguration));
verify(mockRunnerBuilder).graphQLConfiguration(eq(graphQLConfiguration)); verify(mockRunnerBuilder).graphQLConfiguration(eq(graphQLConfiguration));
verify(mockRunnerBuilder).webSocketConfiguration(eq(webSocketConfiguration)); verify(mockRunnerBuilder).webSocketConfiguration(eq(webSocketConfiguration));
@ -1581,8 +1577,8 @@ public class BesuCommandTest extends CommandTestAbstract {
final int maxPeers = 123; final int maxPeers = 123;
parseCommand("--max-peers", String.valueOf(maxPeers)); parseCommand("--max-peers", String.valueOf(maxPeers));
verify(mockRunnerBuilder).maxPeers(intArgumentCaptor.capture()); verify(mockControllerBuilder).maxPeers(intArgumentCaptor.capture());
verify(mockRunnerBuilder).build(); verify(mockControllerBuilder).build();
assertThat(intArgumentCaptor.getValue()).isEqualTo(maxPeers); assertThat(intArgumentCaptor.getValue()).isEqualTo(maxPeers);
@ -1613,10 +1609,10 @@ public class BesuCommandTest extends CommandTestAbstract {
assertThat(commandOutput.toString(UTF_8)).isEmpty(); assertThat(commandOutput.toString(UTF_8)).isEmpty();
assertThat(commandErrorOutput.toString(UTF_8)).isEmpty(); assertThat(commandErrorOutput.toString(UTF_8)).isEmpty();
verify(mockRunnerBuilder).maxPeers(intArgumentCaptor.capture()); verify(mockControllerBuilder).maxPeers(intArgumentCaptor.capture());
assertThat(intArgumentCaptor.getValue()).isEqualTo(maxPeers); assertThat(intArgumentCaptor.getValue()).isEqualTo(maxPeers);
verify(mockRunnerBuilder).minPeers(intArgumentCaptor.capture()); verify(mockControllerBuilder).lowerBoundPeers(intArgumentCaptor.capture());
assertThat(intArgumentCaptor.getValue()).isEqualTo(maxPeers); assertThat(intArgumentCaptor.getValue()).isEqualTo(maxPeers);
verify(mockRunnerBuilder).build(); verify(mockRunnerBuilder).build();
@ -1648,10 +1644,10 @@ public class BesuCommandTest extends CommandTestAbstract {
"--Xp2p-peer-lower-bound", "--Xp2p-peer-lower-bound",
String.valueOf(minPeers)); String.valueOf(minPeers));
verify(mockRunnerBuilder).maxPeers(intArgumentCaptor.capture()); verify(mockControllerBuilder).maxPeers(intArgumentCaptor.capture());
assertThat(intArgumentCaptor.getValue()).isEqualTo(maxPeers); assertThat(intArgumentCaptor.getValue()).isEqualTo(maxPeers);
verify(mockRunnerBuilder).minPeers(intArgumentCaptor.capture()); verify(mockControllerBuilder).lowerBoundPeers(intArgumentCaptor.capture());
assertThat(intArgumentCaptor.getValue()).isEqualTo(minPeers); assertThat(intArgumentCaptor.getValue()).isEqualTo(minPeers);
verify(mockRunnerBuilder).build(); verify(mockRunnerBuilder).build();
@ -1665,16 +1661,16 @@ public class BesuCommandTest extends CommandTestAbstract {
final int remoteConnectionsPercentage = 12; final int remoteConnectionsPercentage = 12;
parseCommand( parseCommand(
"--remote-connections-limit-enabled", "--remote-connections-limit-enabled=true",
"--remote-connections-max-percentage", "--remote-connections-max-percentage",
String.valueOf(remoteConnectionsPercentage)); String.valueOf(remoteConnectionsPercentage));
verify(mockRunnerBuilder).fractionRemoteConnectionsAllowed(floatCaptor.capture()); verify(mockControllerBuilder).maxRemotelyInitiatedPeers(intArgumentCaptor.capture());
verify(mockRunnerBuilder).build(); verify(mockControllerBuilder).build();
assertThat(floatCaptor.getValue()) assertThat(intArgumentCaptor.getValue())
.isEqualTo( .isEqualTo(
Fraction.fromPercentage(Percentage.fromInt(remoteConnectionsPercentage)).getValue()); (int) Math.floor(25 * Fraction.fromPercentage(remoteConnectionsPercentage).getValue()));
assertThat(commandOutput.toString(UTF_8)).isEmpty(); assertThat(commandOutput.toString(UTF_8)).isEmpty();
assertThat(commandErrorOutput.toString(UTF_8)).isEmpty(); assertThat(commandErrorOutput.toString(UTF_8)).isEmpty();
@ -1706,22 +1702,6 @@ public class BesuCommandTest extends CommandTestAbstract {
"should be a number between 0 and 100 inclusive"); "should be a number between 0 and 100 inclusive");
} }
@Test
public void enableRandomConnectionPrioritization() {
parseCommand("--random-peer-priority-enabled");
verify(mockRunnerBuilder).randomPeerPriority(eq(true));
assertThat(commandOutput.toString(UTF_8)).isEmpty();
assertThat(commandErrorOutput.toString(UTF_8)).isEmpty();
}
@Test
public void randomConnectionPrioritizationDisabledByDefault() {
parseCommand();
verify(mockRunnerBuilder).randomPeerPriority(eq(false));
assertThat(commandOutput.toString(UTF_8)).isEmpty();
assertThat(commandErrorOutput.toString(UTF_8)).isEmpty();
}
@Test @Test
public void syncMode_fast() { public void syncMode_fast() {
parseCommand("--sync-mode", "FAST"); parseCommand("--sync-mode", "FAST");
@ -1915,7 +1895,7 @@ public class BesuCommandTest extends CommandTestAbstract {
@Test @Test
public void launcherDefaultOptionValue() { public void launcherDefaultOptionValue() {
TestBesuCommand besuCommand = parseCommand(); final TestBesuCommand besuCommand = parseCommand();
assertThat(besuCommand.getLauncherOptions().isLauncherMode()).isFalse(); assertThat(besuCommand.getLauncherOptions().isLauncherMode()).isFalse();
assertThat(besuCommand.getEnodeDnsConfiguration().updateEnabled()).isFalse(); assertThat(besuCommand.getEnodeDnsConfiguration().updateEnabled()).isFalse();
@ -3379,7 +3359,7 @@ public class BesuCommandTest extends CommandTestAbstract {
@Test @Test
public void rpcWsApiPropertyMustBeUsed() { public void rpcWsApiPropertyMustBeUsed() {
TestBesuCommand command = parseCommand("--rpc-ws-enabled", "--rpc-ws-api", "ETH, NET"); final TestBesuCommand command = parseCommand("--rpc-ws-enabled", "--rpc-ws-api", "ETH, NET");
assertThat(command).isNotNull(); assertThat(command).isNotNull();
verify(mockRunnerBuilder).webSocketConfiguration(wsRpcConfigArgumentCaptor.capture()); verify(mockRunnerBuilder).webSocketConfiguration(wsRpcConfigArgumentCaptor.capture());
@ -5556,6 +5536,20 @@ public class BesuCommandTest extends CommandTestAbstract {
"PoS checkpoint sync can't be used with TTD = 0 and checkpoint totalDifficulty = 0"); "PoS checkpoint sync can't be used with TTD = 0 and checkpoint totalDifficulty = 0");
} }
@Test
public void checkP2pPeerLowerBound_isSet() {
final int lowerBound = 13;
parseCommand("--Xp2p-peer-lower-bound", String.valueOf(lowerBound));
verify(mockControllerBuilder).lowerBoundPeers(intArgumentCaptor.capture());
verify(mockControllerBuilder).build();
assertThat(intArgumentCaptor.getValue()).isEqualTo(lowerBound);
assertThat(commandErrorOutput.toString(UTF_8)).isEmpty();
assertThat(commandOutput.toString(UTF_8)).isEmpty();
}
@Test @Test
public void kzgTrustedSetupFileRequiresDataBlobEnabledNetwork() throws IOException { public void kzgTrustedSetupFileRequiresDataBlobEnabledNetwork() throws IOException {
final Path genesisFileWithoutBlobs = final Path genesisFileWithoutBlobs =

@ -17,7 +17,6 @@ package org.hyperledger.besu.cli;
import static java.nio.charset.StandardCharsets.UTF_8; import static java.nio.charset.StandardCharsets.UTF_8;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.anyString;
@ -228,8 +227,14 @@ public abstract class CommandTestAbstract {
when(mockControllerBuilder.reorgLoggingThreshold(anyLong())).thenReturn(mockControllerBuilder); when(mockControllerBuilder.reorgLoggingThreshold(anyLong())).thenReturn(mockControllerBuilder);
when(mockControllerBuilder.dataStorageConfiguration(any())).thenReturn(mockControllerBuilder); when(mockControllerBuilder.dataStorageConfiguration(any())).thenReturn(mockControllerBuilder);
when(mockControllerBuilder.evmConfiguration(any())).thenReturn(mockControllerBuilder); when(mockControllerBuilder.evmConfiguration(any())).thenReturn(mockControllerBuilder);
when(mockControllerBuilder.networkConfiguration(any())).thenReturn(mockControllerBuilder);
when(mockControllerBuilder.randomPeerPriority(any())).thenReturn(mockControllerBuilder);
when(mockControllerBuilder.maxPeers(anyInt())).thenReturn(mockControllerBuilder); when(mockControllerBuilder.maxPeers(anyInt())).thenReturn(mockControllerBuilder);
when(mockControllerBuilder.chainPruningConfiguration(any())).thenReturn(mockControllerBuilder); when(mockControllerBuilder.chainPruningConfiguration(any())).thenReturn(mockControllerBuilder);
when(mockControllerBuilder.maxPeers(anyInt())).thenReturn(mockControllerBuilder);
when(mockControllerBuilder.lowerBoundPeers(anyInt())).thenReturn(mockControllerBuilder);
when(mockControllerBuilder.maxRemotelyInitiatedPeers(anyInt()))
.thenReturn(mockControllerBuilder);
// doReturn used because of generic BesuController // doReturn used because of generic BesuController
doReturn(mockController).when(mockControllerBuilder).build(); doReturn(mockController).when(mockControllerBuilder).build();
lenient().when(mockController.getProtocolManager()).thenReturn(mockEthProtocolManager); lenient().when(mockController.getProtocolManager()).thenReturn(mockEthProtocolManager);
@ -255,13 +260,6 @@ public abstract class CommandTestAbstract {
when(mockRunnerBuilder.p2pListenPort(anyInt())).thenReturn(mockRunnerBuilder); when(mockRunnerBuilder.p2pListenPort(anyInt())).thenReturn(mockRunnerBuilder);
when(mockRunnerBuilder.p2pListenInterface(anyString())).thenReturn(mockRunnerBuilder); when(mockRunnerBuilder.p2pListenInterface(anyString())).thenReturn(mockRunnerBuilder);
when(mockRunnerBuilder.permissioningConfiguration(any())).thenReturn(mockRunnerBuilder); when(mockRunnerBuilder.permissioningConfiguration(any())).thenReturn(mockRunnerBuilder);
when(mockRunnerBuilder.maxPeers(anyInt())).thenReturn(mockRunnerBuilder);
when(mockRunnerBuilder.minPeers(anyInt())).thenReturn(mockRunnerBuilder);
when(mockRunnerBuilder.limitRemoteWireConnectionsEnabled(anyBoolean()))
.thenReturn(mockRunnerBuilder);
when(mockRunnerBuilder.fractionRemoteConnectionsAllowed(anyFloat()))
.thenReturn(mockRunnerBuilder);
when(mockRunnerBuilder.randomPeerPriority(anyBoolean())).thenReturn(mockRunnerBuilder);
when(mockRunnerBuilder.p2pEnabled(anyBoolean())).thenReturn(mockRunnerBuilder); when(mockRunnerBuilder.p2pEnabled(anyBoolean())).thenReturn(mockRunnerBuilder);
when(mockRunnerBuilder.natMethod(any())).thenReturn(mockRunnerBuilder); when(mockRunnerBuilder.natMethod(any())).thenReturn(mockRunnerBuilder);
when(mockRunnerBuilder.natManagerServiceName(any())).thenReturn(mockRunnerBuilder); when(mockRunnerBuilder.natManagerServiceName(any())).thenReturn(mockRunnerBuilder);
@ -379,7 +377,7 @@ public abstract class CommandTestAbstract {
// reset GlobalOpenTelemetry // reset GlobalOpenTelemetry
GlobalOpenTelemetry.resetForTest(); GlobalOpenTelemetry.resetForTest();
TestBesuCommand besuCommand = getTestBesuCommand(testType); final TestBesuCommand besuCommand = getTestBesuCommand(testType);
besuCommands.add(besuCommand); besuCommands.add(besuCommand);
final File defaultKeyFile = final File defaultKeyFile =

@ -16,7 +16,6 @@ package org.hyperledger.besu.cli.options;
import static java.nio.charset.StandardCharsets.UTF_8; import static java.nio.charset.StandardCharsets.UTF_8;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.hyperledger.besu.cli.DefaultCommandValues.DEFAULT_P2P_PEER_LOWER_BOUND;
import org.hyperledger.besu.cli.options.unstable.NetworkingOptions; import org.hyperledger.besu.cli.options.unstable.NetworkingOptions;
import org.hyperledger.besu.ethereum.p2p.config.NetworkingConfiguration; import org.hyperledger.besu.ethereum.p2p.config.NetworkingConfiguration;
@ -129,32 +128,6 @@ public class NetworkingOptionsTest
assertThat(commandOutput.toString(UTF_8)).isEmpty(); assertThat(commandOutput.toString(UTF_8)).isEmpty();
} }
@Test
public void checkP2pPeerLowerBound_isSet() {
final int lowerBound = 13;
final TestBesuCommand cmd = parseCommand("--Xp2p-peer-lower-bound", String.valueOf(lowerBound));
final NetworkingOptions options = cmd.getNetworkingOptions();
final NetworkingConfiguration networkingConfig = options.toDomainObject();
assertThat(networkingConfig.getRlpx().getPeerLowerBound()).isEqualTo(lowerBound);
assertThat(commandErrorOutput.toString(UTF_8)).isEmpty();
assertThat(commandOutput.toString(UTF_8)).isEmpty();
}
@Test
public void checkP2pPeerLowerBound_isNotSet() {
final TestBesuCommand cmd = parseCommand();
final NetworkingOptions options = cmd.getNetworkingOptions();
final NetworkingConfiguration networkingConfig = options.toDomainObject();
assertThat(networkingConfig.getRlpx().getPeerLowerBound())
.isEqualTo(DEFAULT_P2P_PEER_LOWER_BOUND);
assertThat(commandErrorOutput.toString(UTF_8)).isEmpty();
assertThat(commandOutput.toString(UTF_8)).isEmpty();
}
@Test @Test
public void checkFilterByForkIdNotSet() { public void checkFilterByForkIdNotSet() {
final TestBesuCommand cmd = parseCommand(); final TestBesuCommand cmd = parseCommand();
@ -203,6 +176,7 @@ public class NetworkingOptionsTest
NetworkingConfiguration.DEFAULT_INITIATE_CONNECTIONS_FREQUENCY_SEC + 10); NetworkingConfiguration.DEFAULT_INITIATE_CONNECTIONS_FREQUENCY_SEC + 10);
config.setCheckMaintainedConnectionsFrequency( config.setCheckMaintainedConnectionsFrequency(
NetworkingConfiguration.DEFAULT_CHECK_MAINTAINED_CONNECTIONS_FREQUENCY_SEC + 10); NetworkingConfiguration.DEFAULT_CHECK_MAINTAINED_CONNECTIONS_FREQUENCY_SEC + 10);
config.setPeerLowerBound(NetworkingConfiguration.DEFAULT_PEER_LOWER_BOUND - 10);
return config; return config;
} }

@ -29,6 +29,7 @@ import org.hyperledger.besu.config.GenesisConfigFile;
import org.hyperledger.besu.config.GenesisConfigOptions; import org.hyperledger.besu.config.GenesisConfigOptions;
import org.hyperledger.besu.config.Keccak256ConfigOptions; import org.hyperledger.besu.config.Keccak256ConfigOptions;
import org.hyperledger.besu.cryptoservices.NodeKey; import org.hyperledger.besu.cryptoservices.NodeKey;
import org.hyperledger.besu.cryptoservices.NodeKeyUtils;
import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.ethereum.GasLimitCalculator; import org.hyperledger.besu.ethereum.GasLimitCalculator;
import org.hyperledger.besu.ethereum.bonsai.cache.CachedMerkleTrieLoader; import org.hyperledger.besu.ethereum.bonsai.cache.CachedMerkleTrieLoader;
@ -41,6 +42,7 @@ import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration;
import org.hyperledger.besu.ethereum.eth.sync.SynchronizerConfiguration; import org.hyperledger.besu.ethereum.eth.sync.SynchronizerConfiguration;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration; import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration;
import org.hyperledger.besu.ethereum.mainnet.MainnetBlockHeaderFunctions; import org.hyperledger.besu.ethereum.mainnet.MainnetBlockHeaderFunctions;
import org.hyperledger.besu.ethereum.p2p.config.NetworkingConfiguration;
import org.hyperledger.besu.ethereum.storage.StorageProvider; import org.hyperledger.besu.ethereum.storage.StorageProvider;
import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueSegmentIdentifier; import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueSegmentIdentifier;
import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueStoragePrefixedKeyBlockchainStorage; import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueStoragePrefixedKeyBlockchainStorage;
@ -74,6 +76,7 @@ import org.mockito.junit.MockitoJUnitRunner;
public class BesuControllerBuilderTest { public class BesuControllerBuilderTest {
private BesuControllerBuilder besuControllerBuilder; private BesuControllerBuilder besuControllerBuilder;
private static final NodeKey nodeKey = NodeKeyUtils.generate();
@Mock GenesisConfigFile genesisConfigFile; @Mock GenesisConfigFile genesisConfigFile;
@Mock GenesisConfigOptions genesisConfigOptions; @Mock GenesisConfigOptions genesisConfigOptions;
@ -87,7 +90,6 @@ public class BesuControllerBuilderTest {
@Mock PrivacyParameters privacyParameters; @Mock PrivacyParameters privacyParameters;
@Mock Clock clock; @Mock Clock clock;
@Mock TransactionPoolConfiguration poolConfiguration; @Mock TransactionPoolConfiguration poolConfiguration;
@Mock NodeKey nodeKey;
@Mock StorageProvider storageProvider; @Mock StorageProvider storageProvider;
@Mock GasLimitCalculator gasLimitCalculator; @Mock GasLimitCalculator gasLimitCalculator;
@Mock WorldStateStorage worldStateStorage; @Mock WorldStateStorage worldStateStorage;
@ -157,6 +159,7 @@ public class BesuControllerBuilderTest {
.nodeKey(nodeKey) .nodeKey(nodeKey)
.storageProvider(storageProvider) .storageProvider(storageProvider)
.evmConfiguration(EvmConfiguration.DEFAULT) .evmConfiguration(EvmConfiguration.DEFAULT)
.networkConfiguration(NetworkingConfiguration.create())
.networkId(networkId); .networkId(networkId);
} }

@ -28,6 +28,7 @@ import org.hyperledger.besu.config.GenesisConfigFile;
import org.hyperledger.besu.config.GenesisConfigOptions; import org.hyperledger.besu.config.GenesisConfigOptions;
import org.hyperledger.besu.consensus.merge.MergeContext; import org.hyperledger.besu.consensus.merge.MergeContext;
import org.hyperledger.besu.cryptoservices.NodeKey; import org.hyperledger.besu.cryptoservices.NodeKey;
import org.hyperledger.besu.cryptoservices.NodeKeyUtils;
import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.GasLimitCalculator; import org.hyperledger.besu.ethereum.GasLimitCalculator;
@ -47,6 +48,7 @@ import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfigurati
import org.hyperledger.besu.ethereum.mainnet.MainnetBlockHeaderFunctions; import org.hyperledger.besu.ethereum.mainnet.MainnetBlockHeaderFunctions;
import org.hyperledger.besu.ethereum.mainnet.feemarket.BaseFeeMarket; import org.hyperledger.besu.ethereum.mainnet.feemarket.BaseFeeMarket;
import org.hyperledger.besu.ethereum.mainnet.feemarket.LondonFeeMarket; import org.hyperledger.besu.ethereum.mainnet.feemarket.LondonFeeMarket;
import org.hyperledger.besu.ethereum.p2p.config.NetworkingConfiguration;
import org.hyperledger.besu.ethereum.storage.StorageProvider; import org.hyperledger.besu.ethereum.storage.StorageProvider;
import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueStoragePrefixedKeyBlockchainStorage; import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueStoragePrefixedKeyBlockchainStorage;
import org.hyperledger.besu.ethereum.worldstate.DataStorageFormat; import org.hyperledger.besu.ethereum.worldstate.DataStorageFormat;
@ -79,6 +81,7 @@ import org.mockito.junit.MockitoJUnitRunner;
public class MergeBesuControllerBuilderTest { public class MergeBesuControllerBuilderTest {
private MergeBesuControllerBuilder besuControllerBuilder; private MergeBesuControllerBuilder besuControllerBuilder;
private static final NodeKey nodeKey = NodeKeyUtils.generate();
@Mock GenesisConfigFile genesisConfigFile; @Mock GenesisConfigFile genesisConfigFile;
@Mock GenesisConfigOptions genesisConfigOptions; @Mock GenesisConfigOptions genesisConfigOptions;
@ -90,7 +93,6 @@ public class MergeBesuControllerBuilderTest {
@Mock PrivacyParameters privacyParameters; @Mock PrivacyParameters privacyParameters;
@Mock Clock clock; @Mock Clock clock;
@Mock TransactionPoolConfiguration poolConfiguration; @Mock TransactionPoolConfiguration poolConfiguration;
@Mock NodeKey nodeKey;
@Mock StorageProvider storageProvider; @Mock StorageProvider storageProvider;
@Mock GasLimitCalculator gasLimitCalculator; @Mock GasLimitCalculator gasLimitCalculator;
@Mock WorldStateStorage worldStateStorage; @Mock WorldStateStorage worldStateStorage;
@ -161,6 +163,7 @@ public class MergeBesuControllerBuilderTest {
.nodeKey(nodeKey) .nodeKey(nodeKey)
.storageProvider(storageProvider) .storageProvider(storageProvider)
.evmConfiguration(EvmConfiguration.DEFAULT) .evmConfiguration(EvmConfiguration.DEFAULT)
.networkConfiguration(NetworkingConfiguration.create())
.networkId(networkId); .networkId(networkId);
} }
@ -169,7 +172,7 @@ public class MergeBesuControllerBuilderTest {
when(genesisConfigOptions.getTerminalTotalDifficulty()) when(genesisConfigOptions.getTerminalTotalDifficulty())
.thenReturn(Optional.of(UInt256.valueOf(1500L))); .thenReturn(Optional.of(UInt256.valueOf(1500L)));
Difficulty terminalTotalDifficulty = final Difficulty terminalTotalDifficulty =
visitWithMockConfigs(new MergeBesuControllerBuilder()) visitWithMockConfigs(new MergeBesuControllerBuilder())
.build() .build()
.getProtocolContext() .getProtocolContext()
@ -181,9 +184,9 @@ public class MergeBesuControllerBuilderTest {
@Test @Test
public void assertConfiguredBlock() { public void assertConfiguredBlock() {
Blockchain mockChain = mock(Blockchain.class); final Blockchain mockChain = mock(Blockchain.class);
when(mockChain.getBlockHeader(anyLong())).thenReturn(Optional.of(mock(BlockHeader.class))); when(mockChain.getBlockHeader(anyLong())).thenReturn(Optional.of(mock(BlockHeader.class)));
MergeContext mergeContext = final MergeContext mergeContext =
besuControllerBuilder.createConsensusContext( besuControllerBuilder.createConsensusContext(
mockChain, mockChain,
mock(WorldStateArchive.class), mock(WorldStateArchive.class),
@ -205,10 +208,10 @@ public class MergeBesuControllerBuilderTest {
mock(WorldStateArchive.class), mock(WorldStateArchive.class),
this.besuControllerBuilder.createProtocolSchedule())); this.besuControllerBuilder.createProtocolSchedule()));
assertThat(mergeContext).isNotNull(); assertThat(mergeContext).isNotNull();
Difficulty over = Difficulty.of(10000L); final Difficulty over = Difficulty.of(10000L);
Difficulty under = Difficulty.of(10L); final Difficulty under = Difficulty.of(10L);
BlockHeader parent = final BlockHeader parent =
headerGenerator headerGenerator
.difficulty(under) .difficulty(under)
.parentHash(genesisState.getBlock().getHash()) .parentHash(genesisState.getBlock().getHash())
@ -218,7 +221,7 @@ public class MergeBesuControllerBuilderTest {
.buildHeader(); .buildHeader();
blockchain.appendBlock(new Block(parent, BlockBody.empty()), Collections.emptyList()); blockchain.appendBlock(new Block(parent, BlockBody.empty()), Collections.emptyList());
BlockHeader terminal = final BlockHeader terminal =
headerGenerator headerGenerator
.difficulty(over) .difficulty(over)
.parentHash(parent.getHash()) .parentHash(parent.getHash())
@ -233,9 +236,9 @@ public class MergeBesuControllerBuilderTest {
@Test @Test
public void assertNoFinalizedBlockWhenNotStored() { public void assertNoFinalizedBlockWhenNotStored() {
Blockchain mockChain = mock(Blockchain.class); final Blockchain mockChain = mock(Blockchain.class);
when(mockChain.getFinalized()).thenReturn(Optional.empty()); when(mockChain.getFinalized()).thenReturn(Optional.empty());
MergeContext mergeContext = final MergeContext mergeContext =
besuControllerBuilder.createConsensusContext( besuControllerBuilder.createConsensusContext(
mockChain, mockChain,
mock(WorldStateArchive.class), mock(WorldStateArchive.class),
@ -252,7 +255,7 @@ public class MergeBesuControllerBuilderTest {
when(mockChain.getFinalized()).thenReturn(Optional.of(finalizedHeader.getHash())); when(mockChain.getFinalized()).thenReturn(Optional.of(finalizedHeader.getHash()));
when(mockChain.getBlockHeader(finalizedHeader.getHash())) when(mockChain.getBlockHeader(finalizedHeader.getHash()))
.thenReturn(Optional.of(finalizedHeader)); .thenReturn(Optional.of(finalizedHeader));
MergeContext mergeContext = final MergeContext mergeContext =
besuControllerBuilder.createConsensusContext( besuControllerBuilder.createConsensusContext(
mockChain, mockChain,
mock(WorldStateArchive.class), mock(WorldStateArchive.class),

@ -44,6 +44,7 @@ import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration;
import org.hyperledger.besu.ethereum.eth.sync.SynchronizerConfiguration; import org.hyperledger.besu.ethereum.eth.sync.SynchronizerConfiguration;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration; import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration;
import org.hyperledger.besu.ethereum.mainnet.MainnetBlockHeaderFunctions; import org.hyperledger.besu.ethereum.mainnet.MainnetBlockHeaderFunctions;
import org.hyperledger.besu.ethereum.p2p.config.NetworkingConfiguration;
import org.hyperledger.besu.ethereum.storage.StorageProvider; import org.hyperledger.besu.ethereum.storage.StorageProvider;
import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueStoragePrefixedKeyBlockchainStorage; import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueStoragePrefixedKeyBlockchainStorage;
import org.hyperledger.besu.ethereum.worldstate.DataStorageFormat; import org.hyperledger.besu.ethereum.worldstate.DataStorageFormat;
@ -144,7 +145,8 @@ public class QbftBesuControllerBuilderTest {
.nodeKey(nodeKey) .nodeKey(nodeKey)
.storageProvider(storageProvider) .storageProvider(storageProvider)
.gasLimitCalculator(gasLimitCalculator) .gasLimitCalculator(gasLimitCalculator)
.evmConfiguration(EvmConfiguration.DEFAULT); .evmConfiguration(EvmConfiguration.DEFAULT)
.networkConfiguration(NetworkingConfiguration.create());
} }
@Test @Test

@ -20,6 +20,7 @@ import org.hyperledger.besu.consensus.common.bft.events.BftEvents;
import org.hyperledger.besu.consensus.common.bft.network.PeerConnectionTracker; import org.hyperledger.besu.consensus.common.bft.network.PeerConnectionTracker;
import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.ethereum.p2p.network.ProtocolManager; import org.hyperledger.besu.ethereum.p2p.network.ProtocolManager;
import org.hyperledger.besu.ethereum.p2p.peers.Peer;
import org.hyperledger.besu.ethereum.p2p.rlpx.connections.PeerConnection; import org.hyperledger.besu.ethereum.p2p.rlpx.connections.PeerConnection;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.Capability; import org.hyperledger.besu.ethereum.p2p.rlpx.wire.Capability;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.Message; import org.hyperledger.besu.ethereum.p2p.rlpx.wire.Message;
@ -107,6 +108,11 @@ public class BftProtocolManager implements ProtocolManager {
peers.add(peerConnection); peers.add(peerConnection);
} }
@Override
public boolean shouldConnect(final Peer peer, final boolean incoming) {
return false; // for now the EthProtocolManager takes care of this
}
@Override @Override
public void handleDisconnect( public void handleDisconnect(
final PeerConnection peerConnection, final PeerConnection peerConnection,

@ -42,7 +42,7 @@ public class EthSynchronizerUpdaterTest {
@Test @Test
public void ethPeerIsMissingResultInNoUpdate() { public void ethPeerIsMissingResultInNoUpdate() {
when(ethPeers.peer(any())).thenReturn(null); when(ethPeers.peer(any(PeerConnection.class))).thenReturn(null);
final EthSynchronizerUpdater updater = new EthSynchronizerUpdater(ethPeers); final EthSynchronizerUpdater updater = new EthSynchronizerUpdater(ethPeers);
@ -53,7 +53,7 @@ public class EthSynchronizerUpdaterTest {
@Test @Test
public void chainStateUpdateIsAttemptedIfEthPeerExists() { public void chainStateUpdateIsAttemptedIfEthPeerExists() {
when(ethPeers.peer(any())).thenReturn(ethPeer); when(ethPeers.peer(any(PeerConnection.class))).thenReturn(ethPeer);
when(ethPeer.chainState()).thenReturn(chainState); when(ethPeer.chainState()).thenReturn(chainState);
final EthSynchronizerUpdater updater = new EthSynchronizerUpdater(ethPeers); final EthSynchronizerUpdater updater = new EthSynchronizerUpdater(ethPeers);

@ -91,11 +91,11 @@ public class JsonRpcExecutor {
return rpcProcessor.process( return rpcProcessor.process(
id, method, span, new JsonRpcRequestContext(requestBody, optionalUser, alive)); id, method, span, new JsonRpcRequestContext(requestBody, optionalUser, alive));
} catch (IllegalArgumentException e) { } catch (final IllegalArgumentException e) {
try { try {
final Integer id = jsonRpcRequest.getInteger("id", null); final Integer id = jsonRpcRequest.getInteger("id", null);
return new JsonRpcErrorResponse(id, INVALID_REQUEST); return new JsonRpcErrorResponse(id, INVALID_REQUEST);
} catch (ClassCastException idNotIntegerException) { } catch (final ClassCastException idNotIntegerException) {
return new JsonRpcErrorResponse(null, INVALID_REQUEST); return new JsonRpcErrorResponse(null, INVALID_REQUEST);
} }
} }

@ -79,7 +79,8 @@ public class AdminJsonRpcHttpServiceTest extends JsonRpcHttpServiceTestBase {
List.of(), List.of(),
EthProtocolConfiguration.DEFAULT_MAX_MESSAGE_SIZE, EthProtocolConfiguration.DEFAULT_MAX_MESSAGE_SIZE,
TestClock.fixed(), TestClock.fixed(),
Collections.emptyList())); Collections.emptyList(),
Bytes.random(64)));
peerList.add( peerList.add(
new EthPeer( new EthPeer(
MockPeerConnection.create(info2, addr30301, addr60302), MockPeerConnection.create(info2, addr30301, addr60302),
@ -88,7 +89,8 @@ public class AdminJsonRpcHttpServiceTest extends JsonRpcHttpServiceTestBase {
List.of(), List.of(),
EthProtocolConfiguration.DEFAULT_MAX_MESSAGE_SIZE, EthProtocolConfiguration.DEFAULT_MAX_MESSAGE_SIZE,
TestClock.fixed(), TestClock.fixed(),
Collections.emptyList())); Collections.emptyList(),
Bytes.random(64)));
peerList.add( peerList.add(
new EthPeer( new EthPeer(
MockPeerConnection.create(info3, addr30301, addr60303), MockPeerConnection.create(info3, addr30301, addr60303),
@ -97,7 +99,8 @@ public class AdminJsonRpcHttpServiceTest extends JsonRpcHttpServiceTestBase {
List.of(), List.of(),
EthProtocolConfiguration.DEFAULT_MAX_MESSAGE_SIZE, EthProtocolConfiguration.DEFAULT_MAX_MESSAGE_SIZE,
TestClock.fixed(), TestClock.fixed(),
Collections.emptyList())); Collections.emptyList(),
Bytes.random(64)));
when(ethPeersMock.streamAllPeers()).thenReturn(peerList.stream()); when(ethPeersMock.streamAllPeers()).thenReturn(peerList.stream());
when(peerDiscoveryMock.getPeerCount()).thenReturn(peerList.size()); when(peerDiscoveryMock.getPeerCount()).thenReturn(peerList.size());

@ -64,6 +64,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.stream.Stream;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import io.vertx.core.Vertx; import io.vertx.core.Vertx;
@ -280,6 +281,8 @@ public class JsonRpcHttpServiceRpcApisTest {
.blockchain(blockchain) .blockchain(blockchain)
.blockNumberForks(Collections.emptyList()) .blockNumberForks(Collections.emptyList())
.timestampForks(Collections.emptyList()) .timestampForks(Collections.emptyList())
.allConnectionsSupplier(Stream::empty)
.allActiveConnectionsSupplier(Stream::empty)
.build(); .build();
p2pNetwork.start(); p2pNetwork.start();

@ -17,12 +17,16 @@ package org.hyperledger.besu.ethereum.api.jsonrpc;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import org.hyperledger.besu.ethereum.p2p.peers.DefaultPeer;
import org.hyperledger.besu.ethereum.p2p.peers.EnodeURLImpl; import org.hyperledger.besu.ethereum.p2p.peers.EnodeURLImpl;
import org.hyperledger.besu.ethereum.p2p.peers.Peer;
import org.hyperledger.besu.ethereum.p2p.rlpx.connections.PeerConnection; import org.hyperledger.besu.ethereum.p2p.rlpx.connections.PeerConnection;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.PeerInfo; import org.hyperledger.besu.ethereum.p2p.rlpx.wire.PeerInfo;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import org.apache.tuweni.bytes.Bytes;
public class MockPeerConnection { public class MockPeerConnection {
PeerInfo peerInfo; PeerInfo peerInfo;
InetSocketAddress localAddress; InetSocketAddress localAddress;
@ -32,8 +36,11 @@ public class MockPeerConnection {
final PeerInfo peerInfo, final PeerInfo peerInfo,
final InetSocketAddress localAddress, final InetSocketAddress localAddress,
final InetSocketAddress remoteAddress) { final InetSocketAddress remoteAddress) {
PeerConnection peerConnection = mock(PeerConnection.class); final PeerConnection peerConnection = mock(PeerConnection.class);
when(peerConnection.getPeerInfo()).thenReturn(peerInfo); when(peerConnection.getPeerInfo()).thenReturn(peerInfo);
if (peerInfo != null) {
when(peerConnection.getPeer()).thenReturn(createPeer(peerInfo.getNodeId()));
}
when(peerConnection.getLocalAddress()).thenReturn(localAddress); when(peerConnection.getLocalAddress()).thenReturn(localAddress);
when(peerConnection.getRemoteAddress()).thenReturn(remoteAddress); when(peerConnection.getRemoteAddress()).thenReturn(remoteAddress);
when(peerConnection.getRemoteEnode()) when(peerConnection.getRemoteEnode())
@ -47,4 +54,12 @@ public class MockPeerConnection {
return peerConnection; return peerConnection;
} }
public static EnodeURLImpl.Builder enodeBuilder() {
return EnodeURLImpl.builder().ipAddress("127.0.0.1").useDefaultPorts().nodeId(Peer.randomId());
}
public static Peer createPeer(final Bytes nodeId) {
return DefaultPeer.fromEnodeURL(enodeBuilder().nodeId(nodeId).build());
}
} }

@ -125,7 +125,8 @@ public class AdminPeersTest {
List.of(), List.of(),
EthProtocolConfiguration.DEFAULT_MAX_MESSAGE_SIZE, EthProtocolConfiguration.DEFAULT_MAX_MESSAGE_SIZE,
TestClock.fixed(), TestClock.fixed(),
Collections.emptyList()); Collections.emptyList(),
Bytes.random(64));
return Lists.newArrayList(ethPeer); return Lists.newArrayList(ethPeer);
} }

@ -68,7 +68,7 @@ public class EthPeer implements Comparable<EthPeer> {
private static final int MAX_OUTSTANDING_REQUESTS = 5; private static final int MAX_OUTSTANDING_REQUESTS = 5;
private final PeerConnection connection; private PeerConnection connection;
private final int maxTrackedSeenBlocks = 300; private final int maxTrackedSeenBlocks = 300;
@ -81,6 +81,7 @@ public class EthPeer implements Comparable<EthPeer> {
return size() > maxTrackedSeenBlocks; return size() > maxTrackedSeenBlocks;
} }
})); }));
private final Bytes localNodeId;
private Optional<BlockHeader> checkpointHeader = Optional.empty(); private Optional<BlockHeader> checkpointHeader = Optional.empty();
@ -89,7 +90,7 @@ public class EthPeer implements Comparable<EthPeer> {
private final Clock clock; private final Clock clock;
private final List<NodeMessagePermissioningProvider> permissioningProviders; private final List<NodeMessagePermissioningProvider> permissioningProviders;
private final ChainState chainHeadState = new ChainState(); private final ChainState chainHeadState = new ChainState();
private final AtomicBoolean statusHasBeenSentToPeer = new AtomicBoolean(false); private final AtomicBoolean readyForRequests = new AtomicBoolean(false);
private final AtomicBoolean statusHasBeenReceivedFromPeer = new AtomicBoolean(false); private final AtomicBoolean statusHasBeenReceivedFromPeer = new AtomicBoolean(false);
private final AtomicBoolean fullyValidated = new AtomicBoolean(false); private final AtomicBoolean fullyValidated = new AtomicBoolean(false);
private final AtomicInteger lastProtocolVersion = new AtomicInteger(0); private final AtomicInteger lastProtocolVersion = new AtomicInteger(0);
@ -101,6 +102,7 @@ public class EthPeer implements Comparable<EthPeer> {
private final AtomicReference<Consumer<EthPeer>> onStatusesExchanged = new AtomicReference<>(); private final AtomicReference<Consumer<EthPeer>> onStatusesExchanged = new AtomicReference<>();
private final PeerReputation reputation = new PeerReputation(); private final PeerReputation reputation = new PeerReputation();
private final Map<PeerValidator, Boolean> validationStatus = new ConcurrentHashMap<>(); private final Map<PeerValidator, Boolean> validationStatus = new ConcurrentHashMap<>();
private final Bytes id;
private static final Map<Integer, Integer> roundMessages; private static final Map<Integer, Integer> roundMessages;
@ -126,7 +128,8 @@ public class EthPeer implements Comparable<EthPeer> {
final List<PeerValidator> peerValidators, final List<PeerValidator> peerValidators,
final int maxMessageSize, final int maxMessageSize,
final Clock clock, final Clock clock,
final List<NodeMessagePermissioningProvider> permissioningProviders) { final List<NodeMessagePermissioningProvider> permissioningProviders,
final Bytes localNodeId) {
this.connection = connection; this.connection = connection;
this.protocolName = protocolName; this.protocolName = protocolName;
this.maxMessageSize = maxMessageSize; this.maxMessageSize = maxMessageSize;
@ -137,6 +140,8 @@ public class EthPeer implements Comparable<EthPeer> {
fullyValidated.set(peerValidators.isEmpty()); fullyValidated.set(peerValidators.isEmpty());
this.requestManagers = new ConcurrentHashMap<>(); this.requestManagers = new ConcurrentHashMap<>();
this.localNodeId = localNodeId;
this.id = connection.getPeer().getId();
initEthRequestManagers(); initEthRequestManagers();
initSnapRequestManagers(); initSnapRequestManagers();
@ -228,13 +233,32 @@ public class EthPeer implements Comparable<EthPeer> {
public RequestManager.ResponseStream send( public RequestManager.ResponseStream send(
final MessageData messageData, final String protocolName) throws PeerNotConnected { final MessageData messageData, final String protocolName) throws PeerNotConnected {
if (connection.getAgreedCapabilities().stream() return send(messageData, protocolName, this.connection);
}
/**
* This method is only used for sending the status message, as it is possible that we have
* multiple connections to the same peer at that time.
*
* @param messageData the data to send
* @param protocolName the protocol to use for sending
* @param connectionToUse the connection to use for sending
* @return the response stream from the peer
* @throws PeerNotConnected if the peer is not connected
*/
public RequestManager.ResponseStream send(
final MessageData messageData,
final String protocolName,
final PeerConnection connectionToUse)
throws PeerNotConnected {
if (connectionToUse.getAgreedCapabilities().stream()
.noneMatch(capability -> capability.getName().equalsIgnoreCase(protocolName))) { .noneMatch(capability -> capability.getName().equalsIgnoreCase(protocolName))) {
LOG.debug("Protocol {} unavailable for this peer {}", protocolName, this); LOG.debug("Protocol {} unavailable for this peer {}", protocolName, this);
return null; return null;
} }
if (permissioningProviders.stream() if (permissioningProviders.stream()
.anyMatch(p -> !p.isMessagePermitted(connection.getRemoteEnode(), messageData.getCode()))) { .anyMatch(
p -> !p.isMessagePermitted(connectionToUse.getRemoteEnode(), messageData.getCode()))) {
LOG.info( LOG.info(
"Permissioning blocked sending of message code {} to {}", messageData.getCode(), this); "Permissioning blocked sending of message code {} to {}", messageData.getCode(), this);
if (LOG.isDebugEnabled()) { if (LOG.isDebugEnabled()) {
@ -243,7 +267,8 @@ public class EthPeer implements Comparable<EthPeer> {
permissioningProviders.stream() permissioningProviders.stream()
.filter( .filter(
p -> p ->
!p.isMessagePermitted(connection.getRemoteEnode(), messageData.getCode()))); !p.isMessagePermitted(
connectionToUse.getRemoteEnode(), messageData.getCode())));
} }
return null; return null;
} }
@ -266,7 +291,7 @@ public class EthPeer implements Comparable<EthPeer> {
} }
} }
connection.sendForProtocol(protocolName, messageData); connectionToUse.sendForProtocol(protocolName, messageData);
return null; return null;
} }
@ -433,27 +458,51 @@ public class EthPeer implements Comparable<EthPeer> {
knownBlocks.add(hash); knownBlocks.add(hash);
} }
public void registerStatusSent() { public void registerStatusSent(final PeerConnection connection) {
statusHasBeenSentToPeer.set(true); synchronized (this) {
maybeExecuteStatusesExchangedCallback(); connection.setStatusSent();
maybeExecuteStatusesExchangedCallback(connection);
}
} }
public void registerStatusReceived( public void registerStatusReceived(
final Hash hash, final Difficulty td, final int protocolVersion) { final Hash hash,
final Difficulty td,
final int protocolVersion,
final PeerConnection connection) {
chainHeadState.statusReceived(hash, td); chainHeadState.statusReceived(hash, td);
lastProtocolVersion.set(protocolVersion); lastProtocolVersion.set(protocolVersion);
statusHasBeenReceivedFromPeer.set(true); statusHasBeenReceivedFromPeer.set(true);
maybeExecuteStatusesExchangedCallback(); synchronized (this) {
connection.setStatusReceived();
maybeExecuteStatusesExchangedCallback(connection);
}
} }
private void maybeExecuteStatusesExchangedCallback() { private void maybeExecuteStatusesExchangedCallback(final PeerConnection newConnection) {
if (readyForRequests()) { synchronized (this) {
final Consumer<EthPeer> callback = onStatusesExchanged.getAndSet(null); if (newConnection.getStatusExchanged()) {
if (callback == null) { if (!this.connection.equals(newConnection)) {
return; if (readyForRequests.get()) {
// We have two connections that are ready for requests, figure out which connection to
// keep
if (compareDuplicateConnections(this.connection, newConnection) > 0) {
LOG.trace("Changed connection from {} to {}", this.connection, newConnection);
this.connection = newConnection;
}
} else {
// use the new connection for now, as it is ready for requests, which the "old" one is
// not
this.connection = newConnection;
}
}
readyForRequests.set(true);
final Consumer<EthPeer> peerConsumer = onStatusesExchanged.getAndSet(null);
if (peerConsumer != null) {
LOG.trace("Status message exchange successful. {}", this);
peerConsumer.accept(this);
}
} }
LOG.debug("Status message exchange successful with a peer on a matching chain. {}", this);
callback.accept(this);
} }
} }
@ -463,7 +512,7 @@ public class EthPeer implements Comparable<EthPeer> {
* @return true if the peer is ready to accept requests for data. * @return true if the peer is ready to accept requests for data.
*/ */
public boolean readyForRequests() { public boolean readyForRequests() {
return statusHasBeenSentToPeer.get() && statusHasBeenReceivedFromPeer.get(); return readyForRequests.get();
} }
/** /**
@ -475,15 +524,6 @@ public class EthPeer implements Comparable<EthPeer> {
return statusHasBeenReceivedFromPeer.get(); return statusHasBeenReceivedFromPeer.get();
} }
/**
* Return true if we have sent a status message to this peer.
*
* @return true if we have sent a status message to this peer.
*/
boolean statusHasBeenSentToPeer() {
return statusHasBeenSentToPeer.get();
}
public boolean hasSeenBlock(final Hash hash) { public boolean hasSeenBlock(final Hash hash) {
return knownBlocks.contains(hash); return knownBlocks.contains(hash);
} }
@ -559,7 +599,7 @@ public class EthPeer implements Comparable<EthPeer> {
isFullyValidated(), isFullyValidated(),
isDisconnected(), isDisconnected(),
connection.getPeerInfo().getClientId(), connection.getPeerInfo().getClientId(),
System.identityHashCode(connection), connection,
connection.getPeer().getEnodeURLString()); connection.getPeer().getEnodeURLString());
} }
@ -590,6 +630,41 @@ public class EthPeer implements Comparable<EthPeer> {
return checkpointHeader; return checkpointHeader;
} }
public Bytes getId() {
return id;
}
/**
* Compares two connections to the same peer to determine which connection should be kept
*
* @param a The first connection
* @param b The second connection
* @return A negative value if {@code a} should be kept, a positive value is {@code b} should be
* kept
*/
private int compareDuplicateConnections(final PeerConnection a, final PeerConnection b) {
if (a.isDisconnected() != b.isDisconnected()) {
// One connection has failed - prioritize the one that hasn't failed
return a.isDisconnected() ? 1 : -1;
}
final Bytes peerId = a.getPeer().getId();
// peerId is the id of the other node
if (a.inboundInitiated() != b.inboundInitiated()) {
// If we have connections initiated in different directions, keep the connection initiated
// by the node with the lower id
if (localNodeId.compareTo(peerId) < 0) {
return a.inboundInitiated() ? 1 : -1;
} else {
return a.inboundInitiated() ? -1 : 1;
}
}
// Otherwise, keep older connection
LOG.trace("comparing timestamps " + a.getInitiatedAt() + " with " + b.getInitiatedAt());
return a.getInitiatedAt() < b.getInitiatedAt() ? -1 : 1;
}
@FunctionalInterface @FunctionalInterface
public interface DisconnectCallback { public interface DisconnectCallback {
void onDisconnect(EthPeer peer); void onDisconnect(EthPeer peer);

@ -17,16 +17,19 @@ package org.hyperledger.besu.ethereum.eth.manager;
import org.hyperledger.besu.ethereum.eth.manager.EthPeer.DisconnectCallback; import org.hyperledger.besu.ethereum.eth.manager.EthPeer.DisconnectCallback;
import org.hyperledger.besu.ethereum.eth.peervalidation.PeerValidator; import org.hyperledger.besu.ethereum.eth.peervalidation.PeerValidator;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec; import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec;
import org.hyperledger.besu.ethereum.p2p.peers.Peer;
import org.hyperledger.besu.ethereum.p2p.rlpx.RlpxAgent;
import org.hyperledger.besu.ethereum.p2p.rlpx.connections.PeerConnection; import org.hyperledger.besu.ethereum.p2p.rlpx.connections.PeerConnection;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.messages.DisconnectMessage; import org.hyperledger.besu.ethereum.p2p.rlpx.wire.messages.DisconnectMessage;
import org.hyperledger.besu.metrics.BesuMetricCategory; import org.hyperledger.besu.metrics.BesuMetricCategory;
import org.hyperledger.besu.plugin.services.MetricsSystem; import org.hyperledger.besu.plugin.services.MetricsSystem;
import org.hyperledger.besu.plugin.services.metrics.Counter;
import org.hyperledger.besu.plugin.services.permissioning.NodeMessagePermissioningProvider; import org.hyperledger.besu.plugin.services.permissioning.NodeMessagePermissioningProvider;
import org.hyperledger.besu.util.Subscribers; import org.hyperledger.besu.util.Subscribers;
import java.time.Clock; import java.time.Clock;
import java.time.Duration;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
@ -34,12 +37,18 @@ import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutionException;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.function.Supplier; import java.util.function.Supplier;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.RemovalNotification;
import org.apache.tuweni.bytes.Bytes;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -57,52 +66,64 @@ public class EthPeers {
public static final Comparator<EthPeer> LEAST_TO_MOST_BUSY = public static final Comparator<EthPeer> LEAST_TO_MOST_BUSY =
Comparator.comparing(EthPeer::outstandingRequests) Comparator.comparing(EthPeer::outstandingRequests)
.thenComparing(EthPeer::getLastRequestTimestamp); .thenComparing(EthPeer::getLastRequestTimestamp);
public static final int NODE_ID_LENGTH = 64;
private final Map<PeerConnection, EthPeer> connections = new ConcurrentHashMap<>(); private final Map<Bytes, EthPeer> completeConnections = new ConcurrentHashMap<>();
private final Cache<PeerConnection, EthPeer> incompleteConnections =
CacheBuilder.newBuilder()
.expireAfterWrite(Duration.ofSeconds(20L))
.concurrencyLevel(1)
.removalListener(this::onCacheRemoval)
.build();
private final String protocolName; private final String protocolName;
private final Clock clock; private final Clock clock;
private final List<NodeMessagePermissioningProvider> permissioningProviders; private final List<NodeMessagePermissioningProvider> permissioningProviders;
private final int maxPeers;
private final int maxMessageSize; private final int maxMessageSize;
private final Subscribers<ConnectCallback> connectCallbacks = Subscribers.create(); private final Subscribers<ConnectCallback> connectCallbacks = Subscribers.create();
private final Subscribers<DisconnectCallback> disconnectCallbacks = Subscribers.create(); private final Subscribers<DisconnectCallback> disconnectCallbacks = Subscribers.create();
private final Collection<PendingPeerRequest> pendingRequests = new CopyOnWriteArrayList<>(); private final Collection<PendingPeerRequest> pendingRequests = new CopyOnWriteArrayList<>();
private final int peerLowerBound;
private final int peerUpperBound;
private final int maxRemotelyInitiatedConnections;
private final Boolean randomPeerPriority;
private final Bytes nodeIdMask = Bytes.random(NODE_ID_LENGTH);
private final Supplier<ProtocolSpec> currentProtocolSpecSupplier; private final Supplier<ProtocolSpec> currentProtocolSpecSupplier;
private Comparator<EthPeer> bestPeerComparator; private Comparator<EthPeer> bestPeerComparator;
private final Bytes localNodeId;
private RlpxAgent rlpxAgent;
public EthPeers( private final Counter connectedPeersCounter;
final String protocolName,
final Supplier<ProtocolSpec> currentProtocolSpecSupplier,
final Clock clock,
final MetricsSystem metricsSystem,
final int maxPeers,
final int maxMessageSize) {
this(
protocolName,
currentProtocolSpecSupplier,
clock,
metricsSystem,
maxPeers,
maxMessageSize,
Collections.emptyList());
}
public EthPeers( public EthPeers(
final String protocolName, final String protocolName,
final Supplier<ProtocolSpec> currentProtocolSpecSupplier, final Supplier<ProtocolSpec> currentProtocolSpecSupplier,
final Clock clock, final Clock clock,
final MetricsSystem metricsSystem, final MetricsSystem metricsSystem,
final int maxPeers,
final int maxMessageSize, final int maxMessageSize,
final List<NodeMessagePermissioningProvider> permissioningProviders) { final List<NodeMessagePermissioningProvider> permissioningProviders,
final Bytes localNodeId,
final int peerLowerBound,
final int peerUpperBound,
final int maxRemotelyInitiatedConnections,
final Boolean randomPeerPriority) {
this.protocolName = protocolName; this.protocolName = protocolName;
this.currentProtocolSpecSupplier = currentProtocolSpecSupplier; this.currentProtocolSpecSupplier = currentProtocolSpecSupplier;
this.clock = clock; this.clock = clock;
this.permissioningProviders = permissioningProviders; this.permissioningProviders = permissioningProviders;
this.maxPeers = maxPeers;
this.maxMessageSize = maxMessageSize; this.maxMessageSize = maxMessageSize;
this.bestPeerComparator = HEAVIEST_CHAIN; this.bestPeerComparator = HEAVIEST_CHAIN;
this.localNodeId = localNodeId;
this.peerLowerBound = peerLowerBound;
this.peerUpperBound = peerUpperBound;
this.maxRemotelyInitiatedConnections = maxRemotelyInitiatedConnections;
this.randomPeerPriority = randomPeerPriority;
LOG.trace(
"MaxPeers: {}, Lower Bound: {}, Max Remote: {}",
peerUpperBound,
peerLowerBound,
maxRemotelyInitiatedConnections);
metricsSystem.createIntegerGauge( metricsSystem.createIntegerGauge(
BesuMetricCategory.ETHEREUM, BesuMetricCategory.ETHEREUM,
"peer_count", "peer_count",
@ -113,39 +134,83 @@ public class EthPeers {
"pending_peer_requests_current", "pending_peer_requests_current",
"Number of peer requests currently pending because peers are busy", "Number of peer requests currently pending because peers are busy",
pendingRequests::size); pendingRequests::size);
metricsSystem.createIntegerGauge(
BesuMetricCategory.ETHEREUM,
"peer_limit",
"The maximum number of peers this node allows to connect",
() -> peerUpperBound);
connectedPeersCounter =
metricsSystem.createCounter(
BesuMetricCategory.PEERS, "connected_total", "Total number of peers connected");
}
public void registerNewConnection(
final PeerConnection newConnection, final List<PeerValidator> peerValidators) {
final Bytes id = newConnection.getPeer().getId();
synchronized (this) {
EthPeer ethPeer = completeConnections.get(id);
if (ethPeer == null) {
final Optional<EthPeer> peerInList =
incompleteConnections.asMap().values().stream()
.filter(p -> p.getId().equals(id))
.findFirst();
ethPeer =
peerInList.orElse(
new EthPeer(
newConnection,
protocolName,
this::ethPeerStatusExchanged,
peerValidators,
maxMessageSize,
clock,
permissioningProviders,
localNodeId));
}
incompleteConnections.put(newConnection, ethPeer);
}
} }
public void registerConnection( public int getPeerLowerBound() {
final PeerConnection peerConnection, final List<PeerValidator> peerValidators) { return peerLowerBound;
final EthPeer peer = }
new EthPeer(
peerConnection, @NotNull
protocolName, private List<PeerConnection> getIncompleteConnections(final Bytes id) {
this::invokeConnectionCallbacks, return incompleteConnections.asMap().keySet().stream()
peerValidators, .filter(nrc -> nrc.getPeer().getId().equals(id))
maxMessageSize, .collect(Collectors.toList());
clock, }
permissioningProviders);
connections.putIfAbsent(peerConnection, peer); public boolean registerDisconnect(final PeerConnection connection) {
LOG.debug("Adding new EthPeer {}", peer.nodeId()); final Bytes id = connection.getPeer().getId();
} final EthPeer peer = completeConnections.get(id);
return registerDisconnect(id, peer, connection);
public void registerDisconnect(final PeerConnection connection) { }
final EthPeer peer = connections.remove(connection);
if (peer != null) { private boolean registerDisconnect(
disconnectCallbacks.forEach(callback -> callback.onDisconnect(peer)); final Bytes id, final EthPeer peer, final PeerConnection connection) {
peer.handleDisconnect(); incompleteConnections.invalidate(connection);
abortPendingRequestsAssignedToDisconnectedPeers(); boolean removed = false;
LOG.debug("Disconnected EthPeer {}", peer); if (peer != null && peer.getConnection().equals(connection)) {
if (!peerHasIncompleteConnection(id)) {
removed = completeConnections.remove(id, peer);
disconnectCallbacks.forEach(callback -> callback.onDisconnect(peer));
peer.handleDisconnect();
abortPendingRequestsAssignedToDisconnectedPeers();
LOG.debug("Disconnected EthPeer {}", peer);
}
} }
reattemptPendingPeerRequests(); reattemptPendingPeerRequests();
return removed;
}
private boolean peerHasIncompleteConnection(final Bytes id) {
return getIncompleteConnections(id).stream().anyMatch(conn -> !conn.isDisconnected());
} }
private void abortPendingRequestsAssignedToDisconnectedPeers() { private void abortPendingRequestsAssignedToDisconnectedPeers() {
synchronized (this) { synchronized (this) {
final Iterator<PendingPeerRequest> iterator = pendingRequests.iterator(); for (final PendingPeerRequest request : pendingRequests) {
while (iterator.hasNext()) {
final PendingPeerRequest request = iterator.next();
if (request.getAssignedPeer().map(EthPeer::isDisconnected).orElse(false)) { if (request.getAssignedPeer().map(EthPeer::isDisconnected).orElse(false)) {
request.abort(); request.abort();
} }
@ -153,8 +218,17 @@ public class EthPeers {
} }
} }
public EthPeer peer(final PeerConnection peerConnection) { public EthPeer peer(final PeerConnection connection) {
return connections.get(peerConnection); try {
return incompleteConnections.get(
connection, () -> completeConnections.get(connection.getPeer().getId()));
} catch (final ExecutionException e) {
throw new RuntimeException(e);
}
}
public EthPeer peer(final Bytes peerId) {
return completeConnections.get(peerId);
} }
public PendingPeerRequest executePeerRequest( public PendingPeerRequest executePeerRequest(
@ -179,7 +253,7 @@ public class EthPeers {
public void dispatchMessage( public void dispatchMessage(
final EthPeer peer, final EthMessage ethMessage, final String protocolName) { final EthPeer peer, final EthMessage ethMessage, final String protocolName) {
Optional<RequestManager> maybeRequestManager = peer.dispatch(ethMessage, protocolName); final Optional<RequestManager> maybeRequestManager = peer.dispatch(ethMessage, protocolName);
if (maybeRequestManager.isPresent() && peer.hasAvailableRequestCapacity()) { if (maybeRequestManager.isPresent() && peer.hasAvailableRequestCapacity()) {
reattemptPendingPeerRequests(); reattemptPendingPeerRequests();
} }
@ -216,28 +290,30 @@ public class EthPeers {
} }
public int peerCount() { public int peerCount() {
return connections.size(); removeDisconnectedPeers();
return completeConnections.size();
} }
public int getMaxPeers() { public int getMaxPeers() {
return maxPeers; return peerUpperBound;
} }
public Stream<EthPeer> streamAllPeers() { public Stream<EthPeer> streamAllPeers() {
return connections.values().stream(); return completeConnections.values().stream();
} }
private void removeDisconnectedPeers() { private void removeDisconnectedPeers() {
final Collection<EthPeer> peerStream = connections.values(); completeConnections
for (EthPeer p : peerStream) { .values()
if (p.isDisconnected()) { .forEach(
connections.remove(p.getConnection()); ep -> {
} if (ep.isDisconnected()) {
} registerDisconnect(ep.getId(), ep, ep.getConnection());
}
});
} }
public Stream<EthPeer> streamAvailablePeers() { public Stream<EthPeer> streamAvailablePeers() {
removeDisconnectedPeers();
return streamAllPeers() return streamAllPeers()
.filter(EthPeer::readyForRequests) .filter(EthPeer::readyForRequests)
.filter(peer -> !peer.isDisconnected()); .filter(peer -> !peer.isDisconnected());
@ -269,6 +345,43 @@ public class EthPeers {
return bestPeerComparator; return bestPeerComparator;
} }
public void setRlpxAgent(final RlpxAgent rlpxAgent) {
this.rlpxAgent = rlpxAgent;
}
public Stream<PeerConnection> getAllActiveConnections() {
return completeConnections.values().stream()
.map(EthPeer::getConnection)
.filter(c -> !c.isDisconnected());
}
public Stream<PeerConnection> getAllConnections() {
return Stream.concat(
completeConnections.values().stream().map(EthPeer::getConnection),
incompleteConnections.asMap().keySet().stream())
.distinct()
.filter(c -> !c.isDisconnected());
}
public boolean shouldConnect(final Peer peer, final boolean inbound) {
if (peerCount() >= peerUpperBound) {
return false;
}
final Bytes id = peer.getId();
final EthPeer ethPeer = completeConnections.get(id);
if (ethPeer != null && !ethPeer.isDisconnected()) {
return false;
}
final List<PeerConnection> incompleteConnections = getIncompleteConnections(id);
if (!incompleteConnections.isEmpty()) {
if (incompleteConnections.stream()
.anyMatch(c -> !c.isDisconnected() && (!inbound || (inbound && c.inboundInitiated())))) {
return false;
}
}
return true;
}
public void disconnectWorstUselessPeer() { public void disconnectWorstUselessPeer() {
streamAvailablePeers() streamAvailablePeers()
.sorted(getBestChainComparator()) .sorted(getBestChainComparator())
@ -293,18 +406,173 @@ public class EthPeers {
@Override @Override
public String toString() { public String toString() {
if (connections.isEmpty()) { if (completeConnections.isEmpty()) {
return "0 EthPeers {}"; return "0 EthPeers {}";
} }
final String connectionsList = final String connectionsList =
connections.values().stream() completeConnections.values().stream()
.sorted() .sorted()
.map(EthPeer::toString) .map(EthPeer::toString)
.collect(Collectors.joining(", \n")); .collect(Collectors.joining(", \n"));
return connections.size() + " EthPeers {\n" + connectionsList + '}'; return completeConnections.size() + " EthPeers {\n" + connectionsList + '}';
}
private void ethPeerStatusExchanged(final EthPeer peer) {
if (addPeerToEthPeers(peer)) {
connectedPeersCounter.inc();
connectCallbacks.forEach(cb -> cb.onPeerConnected(peer));
}
}
private int comparePeerPriorities(final EthPeer p1, final EthPeer p2) {
final PeerConnection a = p1.getConnection();
final PeerConnection b = p2.getConnection();
final boolean aCanExceedPeerLimits = canExceedPeerLimits(a);
final boolean bCanExceedPeerLimits = canExceedPeerLimits(b);
if (aCanExceedPeerLimits && !bCanExceedPeerLimits) {
return -1;
} else if (bCanExceedPeerLimits && !aCanExceedPeerLimits) {
return 1;
} else {
return randomPeerPriority
? compareByMaskedNodeId(a, b)
: compareConnectionInitiationTimes(a, b);
}
}
private boolean canExceedPeerLimits(final PeerConnection a) {
if (rlpxAgent == null) {
return true;
}
return rlpxAgent.canExceedConnectionLimits(a.getPeer());
}
private int compareConnectionInitiationTimes(final PeerConnection a, final PeerConnection b) {
return Math.toIntExact(a.getInitiatedAt() - b.getInitiatedAt());
} }
private void invokeConnectionCallbacks(final EthPeer peer) { private int compareByMaskedNodeId(final PeerConnection a, final PeerConnection b) {
connectCallbacks.forEach(cb -> cb.onPeerConnected(peer)); return a.getPeer().getId().xor(nodeIdMask).compareTo(b.getPeer().getId().xor(nodeIdMask));
}
private void enforceRemoteConnectionLimits() {
if (!shouldLimitRemoteConnections() || peerCount() < maxRemotelyInitiatedConnections) {
// Nothing to do
return;
}
getActivePrioritizedPeers()
.filter(p -> p.getConnection().inboundInitiated())
.filter(p -> !canExceedPeerLimits(p.getConnection()))
.skip(maxRemotelyInitiatedConnections)
.forEach(
conn -> {
LOG.trace(
"Too many remotely initiated connections. Disconnect low-priority connection: {}, maxRemote={}",
conn,
maxRemotelyInitiatedConnections);
conn.disconnect(DisconnectMessage.DisconnectReason.TOO_MANY_PEERS);
});
}
private Stream<EthPeer> getActivePrioritizedPeers() {
return completeConnections.values().stream()
.filter(p -> !p.isDisconnected())
.sorted(this::comparePeerPriorities);
}
private void enforceConnectionLimits() {
if (peerCount() < peerUpperBound) {
// Nothing to do - we're under our limits
return;
}
getActivePrioritizedPeers()
.filter(p -> !p.isDisconnected())
.skip(peerUpperBound)
.map(EthPeer::getConnection)
.filter(c -> !canExceedPeerLimits(c))
.forEach(
conn -> {
LOG.trace(
"Too many connections. Disconnect low-priority connection: {}, maxConnections={}",
conn,
peerUpperBound);
conn.disconnect(DisconnectMessage.DisconnectReason.TOO_MANY_PEERS);
});
}
private boolean remoteConnectionLimitReached() {
return shouldLimitRemoteConnections()
&& countUntrustedRemotelyInitiatedConnections() >= maxRemotelyInitiatedConnections;
}
private boolean shouldLimitRemoteConnections() {
return maxRemotelyInitiatedConnections < peerUpperBound;
}
private long countUntrustedRemotelyInitiatedConnections() {
return completeConnections.values().stream()
.map(ep -> ep.getConnection())
.filter(c -> c.inboundInitiated())
.filter(c -> !c.isDisconnected())
.filter(conn -> !canExceedPeerLimits(conn))
.count();
}
private void onCacheRemoval(
final RemovalNotification<PeerConnection, EthPeer> removalNotification) {
if (removalNotification.wasEvicted()) {
final PeerConnection peerConnectionRemoved = removalNotification.getKey();
final PeerConnection peerConnectionOfEthPeer = removalNotification.getValue().getConnection();
if (!peerConnectionRemoved.equals(peerConnectionOfEthPeer)) {
// If this connection is not the connection of the EthPeer by now we can disconnect
peerConnectionRemoved.disconnect(DisconnectMessage.DisconnectReason.ALREADY_CONNECTED);
}
}
}
private boolean addPeerToEthPeers(final EthPeer peer) {
// We have a connection to a peer that is on the right chain and is willing to connect to us.
// Figure out whether we want to keep this peer and add it to the EthPeers connections.
if (completeConnections.containsValue(peer)) {
return false;
}
final PeerConnection connection = peer.getConnection();
final Bytes id = peer.getId();
if (!randomPeerPriority) {
// Disconnect if too many peers
if (!canExceedPeerLimits(connection) && peerCount() >= peerUpperBound) {
LOG.trace(
"Too many peers. Disconnect connection: {}, max connections {}",
connection,
peerUpperBound);
connection.disconnect(DisconnectMessage.DisconnectReason.TOO_MANY_PEERS);
return false;
}
// Disconnect if too many remotely-initiated connections
if (connection.inboundInitiated()
&& !canExceedPeerLimits(connection)
&& remoteConnectionLimitReached()) {
LOG.trace(
"Too many remotely-initiated connections. Disconnect incoming connection: {}, maxRemote={}",
connection,
maxRemotelyInitiatedConnections);
connection.disconnect(DisconnectMessage.DisconnectReason.TOO_MANY_PEERS);
return false;
}
final boolean added = (completeConnections.putIfAbsent(id, peer) == null);
if (added) {
LOG.trace("Added peer {} with connection {} to completeConnections", id, connection);
} else {
LOG.trace("Did not add peer {} with connection {} to completeConnections", id, connection);
}
return added;
} else {
// randomPeerPriority! Add the peer and if there are too many connections fix it
completeConnections.putIfAbsent(id, peer);
enforceRemoteConnectionLimits();
enforceConnectionLimits();
return completeConnections.containsKey(id);
}
} }
} }

@ -34,6 +34,7 @@ import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
import org.hyperledger.besu.ethereum.forkid.ForkId; import org.hyperledger.besu.ethereum.forkid.ForkId;
import org.hyperledger.besu.ethereum.forkid.ForkIdManager; import org.hyperledger.besu.ethereum.forkid.ForkIdManager;
import org.hyperledger.besu.ethereum.p2p.network.ProtocolManager; import org.hyperledger.besu.ethereum.p2p.network.ProtocolManager;
import org.hyperledger.besu.ethereum.p2p.peers.Peer;
import org.hyperledger.besu.ethereum.p2p.rlpx.connections.PeerConnection; import org.hyperledger.besu.ethereum.p2p.rlpx.connections.PeerConnection;
import org.hyperledger.besu.ethereum.p2p.rlpx.connections.PeerConnection.PeerNotConnected; import org.hyperledger.besu.ethereum.p2p.rlpx.connections.PeerConnection.PeerNotConnected;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.Capability; import org.hyperledger.besu.ethereum.p2p.rlpx.wire.Capability;
@ -283,7 +284,7 @@ public class EthProtocolManager implements ProtocolManager, MinedBlockObserver {
// Handle STATUS processing // Handle STATUS processing
if (code == EthPV62.STATUS) { if (code == EthPV62.STATUS) {
handleStatusMessage(ethPeer, messageData); handleStatusMessage(ethPeer, message);
return; return;
} else if (!ethPeer.statusHasBeenReceived()) { } else if (!ethPeer.statusHasBeenReceived()) {
// Peers are required to send status messages before any other message type // Peers are required to send status messages before any other message type
@ -352,17 +353,12 @@ public class EthProtocolManager implements ProtocolManager, MinedBlockObserver {
@Override @Override
public void handleNewConnection(final PeerConnection connection) { public void handleNewConnection(final PeerConnection connection) {
ethPeers.registerConnection(connection, peerValidators); ethPeers.registerNewConnection(connection, peerValidators);
final EthPeer peer = ethPeers.peer(connection); final EthPeer peer = ethPeers.peer(connection);
if (peer.statusHasBeenSentToPeer()) {
return;
}
final Capability cap = connection.capability(getSupportedProtocol()); final Capability cap = connection.capability(getSupportedProtocol());
final ForkId latestForkId = final ForkId latestForkId =
cap.getVersion() >= 64 ? forkIdManager.getForkIdForChainHead() : null; cap.getVersion() >= 64 ? forkIdManager.getForkIdForChainHead() : null;
// TODO: look to consolidate code below if possible
// making status non-final and implementing it above would be one way.
final StatusMessage status = final StatusMessage status =
StatusMessage.create( StatusMessage.create(
cap.getVersion(), cap.getVersion(),
@ -372,42 +368,58 @@ public class EthProtocolManager implements ProtocolManager, MinedBlockObserver {
genesisHash, genesisHash,
latestForkId); latestForkId);
try { try {
LOG.debug("Sending status message to {}.", peer); LOG.trace("Sending status message to {} for connection {}.", peer.getId(), connection);
peer.send(status, getSupportedProtocol()); peer.send(status, getSupportedProtocol(), connection);
peer.registerStatusSent(); peer.registerStatusSent(connection);
} catch (final PeerNotConnected peerNotConnected) { } catch (final PeerNotConnected peerNotConnected) {
// Nothing to do. // Nothing to do.
} }
LOG.trace("{}", ethPeers); LOG.trace("{}", ethPeers);
} }
@Override
public boolean shouldConnect(final Peer peer, final boolean incoming) {
if (peer.getForkId().map(forkId -> forkIdManager.peerCheck(forkId)).orElse(true)) {
LOG.trace("ForkId OK or not available");
if (ethPeers.shouldConnect(peer, incoming)) {
LOG.trace("EthPeers should connect is TRUE");
return true;
}
}
LOG.trace("Should connect in EthProtocolManager returns false");
return false;
}
@Override @Override
public void handleDisconnect( public void handleDisconnect(
final PeerConnection connection, final PeerConnection connection,
final DisconnectReason reason, final DisconnectReason reason,
final boolean initiatedByPeer) { final boolean initiatedByPeer) {
ethPeers.registerDisconnect(connection); if (ethPeers.registerDisconnect(connection)) {
LOG.debug( LOG.debug(
"Disconnect - {} - {} - {} - {} peers left", "Disconnect - {} - {} - {} - {} peers left\n{}",
initiatedByPeer ? "Inbound" : "Outbound", initiatedByPeer ? "Inbound" : "Outbound",
reason, reason,
connection.getPeerInfo(), connection.getPeer().getId(),
ethPeers.peerCount()); ethPeers.peerCount(),
LOG.trace("{}", ethPeers); ethPeers);
}
} }
private void handleStatusMessage(final EthPeer peer, final MessageData data) { private void handleStatusMessage(final EthPeer peer, final Message message) {
final StatusMessage status = StatusMessage.readFrom(data); final StatusMessage status = StatusMessage.readFrom(message.getData());
final ForkId forkId = status.forkId();
peer.getConnection().getPeer().setForkId(forkId);
try { try {
if (!status.networkId().equals(networkId)) { if (!status.networkId().equals(networkId)) {
LOG.debug("Mismatched network id: {}, EthPeer {}", status.networkId(), peer); LOG.debug("Mismatched network id: {}, EthPeer {}", status.networkId(), peer);
peer.disconnect(DisconnectReason.SUBPROTOCOL_TRIGGERED); peer.disconnect(DisconnectReason.SUBPROTOCOL_TRIGGERED);
} else if (!forkIdManager.peerCheck(status.forkId()) && status.protocolVersion() > 63) { } else if (!forkIdManager.peerCheck(forkId) && status.protocolVersion() > 63) {
LOG.debug( LOG.debug(
"{} has matching network id ({}), but non-matching fork id: {}", "{} has matching network id ({}), but non-matching fork id: {}",
peer, peer,
networkId, networkId,
status.forkId()); forkId);
peer.disconnect(DisconnectReason.SUBPROTOCOL_TRIGGERED); peer.disconnect(DisconnectReason.SUBPROTOCOL_TRIGGERED);
} else if (forkIdManager.peerCheck(status.genesisHash())) { } else if (forkIdManager.peerCheck(status.genesisHash())) {
LOG.debug( LOG.debug(
@ -421,9 +433,16 @@ public class EthProtocolManager implements ProtocolManager, MinedBlockObserver {
LOG.debug("Post-merge disconnect: peer still PoW {}", peer); LOG.debug("Post-merge disconnect: peer still PoW {}", peer);
handleDisconnect(peer.getConnection(), DisconnectReason.SUBPROTOCOL_TRIGGERED, false); handleDisconnect(peer.getConnection(), DisconnectReason.SUBPROTOCOL_TRIGGERED, false);
} else { } else {
LOG.debug("Received status message from {}: {}", peer, status); LOG.debug(
"Received status message from {}: {} with connection {}",
peer,
status,
message.getConnection());
peer.registerStatusReceived( peer.registerStatusReceived(
status.bestHash(), status.totalDifficulty(), status.protocolVersion()); status.bestHash(),
status.totalDifficulty(),
status.protocolVersion(),
message.getConnection());
} }
} catch (final RLPException e) { } catch (final RLPException e) {
LOG.debug("Unable to parse status message from peer {}.", peer, e); LOG.debug("Unable to parse status message from peer {}.", peer, e);

@ -21,6 +21,7 @@ import org.hyperledger.besu.ethereum.eth.manager.EthPeer;
import org.hyperledger.besu.ethereum.eth.manager.EthPeers; import org.hyperledger.besu.ethereum.eth.manager.EthPeers;
import org.hyperledger.besu.ethereum.eth.peervalidation.PeerValidator; import org.hyperledger.besu.ethereum.eth.peervalidation.PeerValidator;
import org.hyperledger.besu.ethereum.p2p.network.ProtocolManager; import org.hyperledger.besu.ethereum.p2p.network.ProtocolManager;
import org.hyperledger.besu.ethereum.p2p.peers.Peer;
import org.hyperledger.besu.ethereum.p2p.rlpx.connections.PeerConnection; import org.hyperledger.besu.ethereum.p2p.rlpx.connections.PeerConnection;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.AbstractSnapMessageData; import org.hyperledger.besu.ethereum.p2p.rlpx.wire.AbstractSnapMessageData;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.Capability; import org.hyperledger.besu.ethereum.p2p.rlpx.wire.Capability;
@ -137,6 +138,11 @@ public class SnapProtocolManager implements ProtocolManager {
@Override @Override
public void handleNewConnection(final PeerConnection connection) {} public void handleNewConnection(final PeerConnection connection) {}
@Override
public boolean shouldConnect(final Peer peer, final boolean incoming) {
return false; // EthManager is taking care of this for now
}
@Override @Override
public void handleDisconnect( public void handleDisconnect(
final PeerConnection connection, final PeerConnection connection,

@ -65,7 +65,7 @@ public class TrailingPeerLimiter implements BlockAddedObserver {
while (!trailingPeers.isEmpty() && trailingPeers.size() > maxTrailingPeers) { while (!trailingPeers.isEmpty() && trailingPeers.size() > maxTrailingPeers) {
final EthPeer peerToDisconnect = trailingPeers.remove(0); final EthPeer peerToDisconnect = trailingPeers.remove(0);
LOG.debug("Enforcing trailing peers limit by disconnecting {}", peerToDisconnect); LOG.debug("Enforcing trailing peers limit by disconnecting {}", peerToDisconnect);
peerToDisconnect.disconnect(DisconnectReason.TOO_MANY_PEERS); peerToDisconnect.disconnect(DisconnectReason.USELESS_PEER);
} }
} }

@ -40,6 +40,8 @@ public class MockPeerConnection implements PeerConnection {
private final Peer peer; private final Peer peer;
private final PeerInfo peerInfo; private final PeerInfo peerInfo;
private Optional<DisconnectReason> disconnectReason = Optional.empty(); private Optional<DisconnectReason> disconnectReason = Optional.empty();
private boolean statusSent;
private boolean statusReceived;
public MockPeerConnection(final Set<Capability> caps, final PeerSendHandler onSend) { public MockPeerConnection(final Set<Capability> caps, final PeerSendHandler onSend) {
this.caps = caps; this.caps = caps;
@ -111,11 +113,36 @@ public class MockPeerConnection implements PeerConnection {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@Override
public long getInitiatedAt() {
return 0;
}
@Override
public boolean inboundInitiated() {
return false;
}
@Override @Override
public boolean isDisconnected() { public boolean isDisconnected() {
return disconnected; return disconnected;
} }
@Override
public void setStatusSent() {
this.statusSent = true;
}
@Override
public void setStatusReceived() {
this.statusReceived = true;
}
@Override
public boolean getStatusExchanged() {
return statusSent && statusReceived;
}
@FunctionalInterface @FunctionalInterface
public interface PeerSendHandler { public interface PeerSendHandler {
void exec(Capability cap, MessageData msg, PeerConnection connection); void exec(Capability cap, MessageData msg, PeerConnection connection);

@ -0,0 +1,32 @@
/*
* 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.eth;
import org.hyperledger.besu.ethereum.p2p.peers.DefaultPeer;
import org.hyperledger.besu.ethereum.p2p.peers.EnodeURLImpl;
import org.hyperledger.besu.ethereum.p2p.peers.Peer;
import org.apache.tuweni.bytes.Bytes;
public class EthPeerTestUtil {
public static EnodeURLImpl.Builder enodeBuilder() {
return EnodeURLImpl.builder().ipAddress("127.0.0.1").useDefaultPorts().nodeId(Peer.randomId());
}
public static Peer createPeer(final Bytes nodeId) {
return DefaultPeer.fromEnodeURL(enodeBuilder().nodeId(nodeId).build());
}
}

@ -18,15 +18,14 @@ import static java.util.Arrays.asList;
import static java.util.Collections.emptyList; import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList; import static java.util.Collections.singletonList;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.fail;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import org.hyperledger.besu.ethereum.core.BlockDataGenerator; import org.hyperledger.besu.ethereum.core.BlockDataGenerator;
import org.hyperledger.besu.ethereum.eth.EthPeerTestUtil;
import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration; import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration;
import org.hyperledger.besu.ethereum.eth.messages.BlockBodiesMessage; import org.hyperledger.besu.ethereum.eth.messages.BlockBodiesMessage;
import org.hyperledger.besu.ethereum.eth.messages.BlockHeadersMessage; import org.hyperledger.besu.ethereum.eth.messages.BlockHeadersMessage;
@ -35,6 +34,7 @@ import org.hyperledger.besu.ethereum.eth.messages.ReceiptsMessage;
import org.hyperledger.besu.ethereum.eth.peervalidation.PeerValidator; import org.hyperledger.besu.ethereum.eth.peervalidation.PeerValidator;
import org.hyperledger.besu.ethereum.p2p.rlpx.connections.PeerConnection; import org.hyperledger.besu.ethereum.p2p.rlpx.connections.PeerConnection;
import org.hyperledger.besu.ethereum.p2p.rlpx.connections.PeerConnection.PeerNotConnected; import org.hyperledger.besu.ethereum.p2p.rlpx.connections.PeerConnection.PeerNotConnected;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.Capability;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.MessageData; import org.hyperledger.besu.ethereum.p2p.rlpx.wire.MessageData;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.PeerInfo; import org.hyperledger.besu.ethereum.p2p.rlpx.wire.PeerInfo;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.messages.PingMessage; import org.hyperledger.besu.ethereum.p2p.rlpx.wire.messages.PingMessage;
@ -43,7 +43,9 @@ import org.hyperledger.besu.testutil.TestClock;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -55,7 +57,10 @@ import org.junit.Test;
public class EthPeerTest { public class EthPeerTest {
private static final BlockDataGenerator gen = new BlockDataGenerator(); private static final BlockDataGenerator gen = new BlockDataGenerator();
private final TestClock clock = new TestClock(); private final TestClock clock = new TestClock();
private static final Bytes NODE_ID = Bytes.random(32); private static final Bytes NODE_ID = Bytes.random(64);
private static final Bytes NODE_ID_ZERO =
Bytes.fromHexString(
"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010");
@Test @Test
public void getHeadersStream() throws PeerNotConnected { public void getHeadersStream() throws PeerNotConnected {
@ -340,10 +345,10 @@ public class EthPeerTest {
when(trueProvider.isMessagePermitted(any(), anyInt())).thenReturn(true); when(trueProvider.isMessagePermitted(any(), anyInt())).thenReturn(true);
when(falseProvider.isMessagePermitted(any(), anyInt())).thenReturn(false); when(falseProvider.isMessagePermitted(any(), anyInt())).thenReturn(false);
final EthPeer peer = createPeer(Collections.emptyList(), List.of(falseProvider, trueProvider)); // use failOnSend callback
final EthPeer peer =
createPeer(Collections.emptyList(), List.of(falseProvider, trueProvider), getFailOnSend());
peer.send(PingMessage.get()); peer.send(PingMessage.get());
verify(peer.getConnection(), times(0)).sendForProtocol(any(), eq(PingMessage.get()));
} }
@Test @Test
@ -357,13 +362,13 @@ public class EthPeerTest {
@Test @Test
public void compareTo_withDifferentNodeId() { public void compareTo_withDifferentNodeId() {
final EthPeer peer1 = createPeerWithPeerInfo(NODE_ID); final EthPeer peer1 = createPeerWithPeerInfo(NODE_ID);
final EthPeer peer2 = createPeerWithPeerInfo(Bytes.fromHexString("0x00")); final EthPeer peer2 = createPeerWithPeerInfo(NODE_ID_ZERO);
assertThat(peer1.compareTo(peer2)).isEqualTo(1); assertThat(peer1.compareTo(peer2)).isEqualTo(1);
assertThat(peer2.compareTo(peer1)).isEqualTo(-1); assertThat(peer2.compareTo(peer1)).isEqualTo(-1);
} }
@Test @Test
public void recordUsefullResponse() { public void recordUsefulResponse() {
final EthPeer peer = createPeer(); final EthPeer peer = createPeer();
peer.recordUselessResponse("bodies"); peer.recordUselessResponse("bodies");
final EthPeer peer2 = createPeer(); final EthPeer peer2 = createPeer();
@ -463,11 +468,13 @@ public class EthPeerTest {
private EthPeer createPeerWithPeerInfo(final Bytes nodeId) { private EthPeer createPeerWithPeerInfo(final Bytes nodeId) {
final PeerConnection peerConnection = mock(PeerConnection.class); final PeerConnection peerConnection = mock(PeerConnection.class);
final Consumer<EthPeer> onPeerReady = (peer) -> {};
// Use a non-eth protocol name to ensure that EthPeer with sub-protocols such as Istanbul // Use a non-eth protocol name to ensure that EthPeer with sub-protocols such as Istanbul
// that extend the sub-protocol work correctly // that extend the sub-protocol work correctly
PeerInfo peerInfo = new PeerInfo(1, "clientId", Collections.emptyList(), 30303, nodeId); PeerInfo peerInfo = new PeerInfo(1, "clientId", Collections.emptyList(), 30303, nodeId);
when(peerConnection.getPeerInfo()).thenReturn(peerInfo); when(peerConnection.getPeerInfo()).thenReturn(peerInfo);
when(peerConnection.getPeer()).thenReturn(EthPeerTestUtil.createPeer(peerInfo.getNodeId()));
final Consumer<EthPeer> onPeerReady = (peer) -> {};
return new EthPeer( return new EthPeer(
peerConnection, peerConnection,
"foo", "foo",
@ -475,13 +482,32 @@ public class EthPeerTest {
Collections.emptyList(), Collections.emptyList(),
EthProtocolConfiguration.DEFAULT_MAX_MESSAGE_SIZE, EthProtocolConfiguration.DEFAULT_MAX_MESSAGE_SIZE,
clock, clock,
Collections.emptyList()); Collections.emptyList(),
Bytes.random(64));
}
private MockPeerConnection.PeerSendHandler getFailOnSend() {
return (cap, message, conn) -> {
fail("should not call send");
};
}
private MockPeerConnection.PeerSendHandler getNoOpSend() {
return (cap, msg, conn) -> {};
} }
private EthPeer createPeer( private EthPeer createPeer(
final List<PeerValidator> peerValidators, final List<PeerValidator> peerValidators,
final List<NodeMessagePermissioningProvider> permissioningProviders) { final List<NodeMessagePermissioningProvider> permissioningProviders) {
final PeerConnection peerConnection = mock(PeerConnection.class); return createPeer(peerValidators, permissioningProviders, getNoOpSend());
}
private EthPeer createPeer(
final List<PeerValidator> peerValidators,
final List<NodeMessagePermissioningProvider> permissioningProviders,
final MockPeerConnection.PeerSendHandler onSend) {
final PeerConnection peerConnection = getPeerConnection(onSend);
final Consumer<EthPeer> onPeerReady = (peer) -> {}; final Consumer<EthPeer> onPeerReady = (peer) -> {};
// Use a non-eth protocol name to ensure that EthPeer with sub-protocols such as Istanbul // Use a non-eth protocol name to ensure that EthPeer with sub-protocols such as Istanbul
// that extend the sub-protocol work correctly // that extend the sub-protocol work correctly
@ -492,7 +518,17 @@ public class EthPeerTest {
peerValidators, peerValidators,
EthProtocolConfiguration.DEFAULT_MAX_MESSAGE_SIZE, EthProtocolConfiguration.DEFAULT_MAX_MESSAGE_SIZE,
clock, clock,
permissioningProviders); permissioningProviders,
Bytes.random(64));
}
private PeerConnection getPeerConnection(final MockPeerConnection.PeerSendHandler onSend) {
// Use a non-eth protocol name to ensure that EthPeer with sub-protocols such as Istanbul
// that extend the sub-protocol work correctly
final Set<Capability> caps =
new HashSet<>(Collections.singletonList(Capability.create("foo", 63)));
return new MockPeerConnection(caps, onSend);
} }
@FunctionalInterface @FunctionalInterface

@ -337,22 +337,6 @@ public class EthPeersTest {
assertThat(peerToDisconnect.getEthPeer().isDisconnected()).isTrue(); // peer is disconnected assertThat(peerToDisconnect.getEthPeer().isDisconnected()).isTrue(); // peer is disconnected
} }
@Test
public void removeClosedConnectionWhenStreamAvailablePeers() {
final RespondingEthPeer peer = EthProtocolManagerTestUtil.createPeer(ethProtocolManager, 1000);
// Check connection is there and connection was not removed
ethPeers.streamAvailablePeers();
assertThat(ethPeers.streamAllPeers().count()).isEqualTo(1);
// Disconnect peer
peer.getEthPeer().disconnect(DisconnectReason.UNKNOWN);
// Call method again, connection should be removed
ethPeers.streamAvailablePeers();
assertThat(ethPeers.streamAllPeers().count()).isEqualTo(0);
}
@Test @Test
public void toString_hasExpectedInfo() { public void toString_hasExpectedInfo() {
assertThat(ethPeers.toString()).isEqualTo("0 EthPeers {}"); assertThat(ethPeers.toString()).isEqualTo("0 EthPeers {}");
@ -360,7 +344,7 @@ public class EthPeersTest {
final EthPeer peerA = final EthPeer peerA =
EthProtocolManagerTestUtil.createPeer(ethProtocolManager, Difficulty.of(50), 20) EthProtocolManagerTestUtil.createPeer(ethProtocolManager, Difficulty.of(50), 20)
.getEthPeer(); .getEthPeer();
ethPeers.registerConnection(peerA.getConnection(), Collections.emptyList()); ethPeers.registerNewConnection(peerA.getConnection(), Collections.emptyList());
assertThat(ethPeers.toString()).contains("1 EthPeers {"); assertThat(ethPeers.toString()).contains("1 EthPeers {");
assertThat(ethPeers.toString()).contains(peerA.getShortNodeId()); assertThat(ethPeers.toString()).contains(peerA.getShortNodeId());
} }

@ -28,6 +28,7 @@ import org.hyperledger.besu.ethereum.core.ProtocolScheduleFixture;
import org.hyperledger.besu.ethereum.eth.EthProtocol; import org.hyperledger.besu.ethereum.eth.EthProtocol;
import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration; import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration;
import org.hyperledger.besu.ethereum.eth.manager.DeterministicEthScheduler.TimeoutPolicy; import org.hyperledger.besu.ethereum.eth.manager.DeterministicEthScheduler.TimeoutPolicy;
import org.hyperledger.besu.ethereum.eth.manager.snap.SnapProtocolManager;
import org.hyperledger.besu.ethereum.eth.peervalidation.PeerValidator; import org.hyperledger.besu.ethereum.eth.peervalidation.PeerValidator;
import org.hyperledger.besu.ethereum.eth.sync.SynchronizerConfiguration; import org.hyperledger.besu.ethereum.eth.sync.SynchronizerConfiguration;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool; import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
@ -45,6 +46,8 @@ import java.util.Collections;
import java.util.Optional; import java.util.Optional;
import java.util.OptionalLong; import java.util.OptionalLong;
import org.apache.tuweni.bytes.Bytes;
public class EthProtocolManagerTestUtil { public class EthProtocolManagerTestUtil {
public static EthProtocolManager create( public static EthProtocolManager create(
@ -71,17 +74,22 @@ public class EthProtocolManagerTestUtil {
final EthProtocolConfiguration ethereumWireProtocolConfiguration, final EthProtocolConfiguration ethereumWireProtocolConfiguration,
final Optional<MergePeerFilter> mergePeerFilter) { final Optional<MergePeerFilter> mergePeerFilter) {
EthPeers peers = final EthPeers peers =
new EthPeers( new EthPeers(
EthProtocol.NAME, EthProtocol.NAME,
() -> protocolSchedule.getByBlockHeader(blockchain.getChainHeadHeader()), () -> protocolSchedule.getByBlockHeader(blockchain.getChainHeadHeader()),
TestClock.fixed(), TestClock.fixed(),
new NoOpMetricsSystem(), new NoOpMetricsSystem(),
EthProtocolConfiguration.DEFAULT_MAX_MESSAGE_SIZE,
Collections.emptyList(),
Bytes.random(64),
25,
25,
25, 25,
EthProtocolConfiguration.DEFAULT_MAX_MESSAGE_SIZE); false);
EthMessages messages = new EthMessages(); final EthMessages messages = new EthMessages();
EthScheduler ethScheduler = new DeterministicEthScheduler(TimeoutPolicy.NEVER_TIMEOUT); final EthScheduler ethScheduler = new DeterministicEthScheduler(TimeoutPolicy.NEVER_TIMEOUT);
EthContext ethContext = new EthContext(peers, messages, ethScheduler); final EthContext ethContext = new EthContext(peers, messages, ethScheduler);
return new EthProtocolManager( return new EthProtocolManager(
blockchain, blockchain,
@ -185,15 +193,21 @@ public class EthProtocolManagerTestUtil {
final WorldStateArchive worldStateArchive, final WorldStateArchive worldStateArchive,
final TransactionPool transactionPool, final TransactionPool transactionPool,
final EthProtocolConfiguration configuration) { final EthProtocolConfiguration configuration) {
EthPeers peers =
final EthPeers peers =
new EthPeers( new EthPeers(
EthProtocol.NAME, EthProtocol.NAME,
() -> protocolSchedule.getByBlockHeader(blockchain.getChainHeadHeader()), () -> protocolSchedule.getByBlockHeader(blockchain.getChainHeadHeader()),
TestClock.fixed(), TestClock.fixed(),
new NoOpMetricsSystem(), new NoOpMetricsSystem(),
EthProtocolConfiguration.DEFAULT_MAX_MESSAGE_SIZE,
Collections.emptyList(),
Bytes.random(64),
25,
25, 25,
EthProtocolConfiguration.DEFAULT_MAX_MESSAGE_SIZE); 25,
EthMessages messages = new EthMessages(); false);
final EthMessages messages = new EthMessages();
return create( return create(
blockchain, blockchain,
@ -214,15 +228,21 @@ public class EthProtocolManagerTestUtil {
final TransactionPool transactionPool, final TransactionPool transactionPool,
final EthProtocolConfiguration configuration, final EthProtocolConfiguration configuration,
final ForkIdManager forkIdManager) { final ForkIdManager forkIdManager) {
EthPeers peers =
final EthPeers peers =
new EthPeers( new EthPeers(
EthProtocol.NAME, EthProtocol.NAME,
() -> protocolSchedule.getByBlockHeader(blockchain.getChainHeadHeader()), () -> protocolSchedule.getByBlockHeader(blockchain.getChainHeadHeader()),
TestClock.fixed(), TestClock.fixed(),
new NoOpMetricsSystem(), new NoOpMetricsSystem(),
EthProtocolConfiguration.DEFAULT_MAX_MESSAGE_SIZE,
Collections.emptyList(),
Bytes.random(64),
25,
25, 25,
EthProtocolConfiguration.DEFAULT_MAX_MESSAGE_SIZE); 25,
EthMessages messages = new EthMessages(); false);
final EthMessages messages = new EthMessages();
return create( return create(
blockchain, blockchain,
@ -240,15 +260,20 @@ public class EthProtocolManagerTestUtil {
final ProtocolSchedule protocolSchedule, final ProtocolSchedule protocolSchedule,
final Blockchain blockchain, final Blockchain blockchain,
final EthScheduler ethScheduler) { final EthScheduler ethScheduler) {
EthPeers peers = final EthPeers peers =
new EthPeers( new EthPeers(
EthProtocol.NAME, EthProtocol.NAME,
() -> protocolSchedule.getByBlockHeader(blockchain.getChainHeadHeader()), () -> protocolSchedule.getByBlockHeader(blockchain.getChainHeadHeader()),
TestClock.fixed(), TestClock.fixed(),
new NoOpMetricsSystem(), new NoOpMetricsSystem(),
EthProtocolConfiguration.DEFAULT_MAX_MESSAGE_SIZE,
Collections.emptyList(),
Bytes.random(64),
25,
25, 25,
EthProtocolConfiguration.DEFAULT_MAX_MESSAGE_SIZE); 25,
EthMessages messages = new EthMessages(); false);
final EthMessages messages = new EthMessages();
return create( return create(
blockchain, blockchain,
@ -392,6 +417,17 @@ public class EthProtocolManagerTestUtil {
.build(); .build();
} }
public static RespondingEthPeer createPeer(
final EthProtocolManager ethProtocolManager,
final SnapProtocolManager snapProtocolManager,
final long estimatedHeight) {
return RespondingEthPeer.builder()
.ethProtocolManager(ethProtocolManager)
.estimatedHeight(estimatedHeight)
.snapProtocolManager(snapProtocolManager)
.build();
}
public static RespondingEthPeer createPeer( public static RespondingEthPeer createPeer(
final EthProtocolManager ethProtocolManager, final EthProtocolManager ethProtocolManager,
final long estimatedHeight, final long estimatedHeight,

@ -308,7 +308,8 @@ public class RequestManagerTest {
Collections.emptyList(), Collections.emptyList(),
EthProtocolConfiguration.DEFAULT_MAX_MESSAGE_SIZE, EthProtocolConfiguration.DEFAULT_MAX_MESSAGE_SIZE,
TestClock.fixed(), TestClock.fixed(),
Collections.emptyList()); Collections.emptyList(),
Bytes.random(64));
} }
@Test @Test

@ -129,11 +129,11 @@ public class RespondingEthPeer {
final MockPeerConnection peerConnection = final MockPeerConnection peerConnection =
new MockPeerConnection( new MockPeerConnection(
caps, (cap, msg, conn) -> outgoingMessages.add(new OutgoingMessage(cap, msg))); caps, (cap, msg, conn) -> outgoingMessages.add(new OutgoingMessage(cap, msg)));
ethPeers.registerConnection(peerConnection, peerValidators); ethPeers.registerNewConnection(peerConnection, peerValidators);
final EthPeer peer = ethPeers.peer(peerConnection); final EthPeer peer = ethPeers.peer(peerConnection);
peer.registerStatusReceived(chainHeadHash, totalDifficulty, 63); peer.registerStatusReceived(chainHeadHash, totalDifficulty, 63, peerConnection);
estimatedHeight.ifPresent(height -> peer.chainState().update(chainHeadHash, height)); estimatedHeight.ifPresent(height -> peer.chainState().update(chainHeadHash, height));
peer.registerStatusSent(); peer.registerStatusSent(peerConnection);
return new RespondingEthPeer( return new RespondingEthPeer(
ethProtocolManager, snapProtocolManager, peerConnection, peer, outgoingMessages); ethProtocolManager, snapProtocolManager, peerConnection, peer, outgoingMessages);

@ -45,11 +45,13 @@ import org.hyperledger.besu.plugin.services.MetricsSystem;
import org.hyperledger.besu.testutil.TestClock; import org.hyperledger.besu.testutil.TestClock;
import java.time.ZoneId; import java.time.ZoneId;
import java.util.Collections;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import org.apache.tuweni.bytes.Bytes;
import org.junit.Before; import org.junit.Before;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
@ -93,8 +95,14 @@ public abstract class AbstractMessageTaskTest<T, R> {
() -> protocolSchedule.getByBlockHeader(blockchain.getChainHeadHeader()), () -> protocolSchedule.getByBlockHeader(blockchain.getChainHeadHeader()),
TestClock.fixed(), TestClock.fixed(),
metricsSystem, metricsSystem,
EthProtocolConfiguration.DEFAULT_MAX_MESSAGE_SIZE,
Collections.emptyList(),
Bytes.random(64),
MAX_PEERS, MAX_PEERS,
EthProtocolConfiguration.DEFAULT_MAX_MESSAGE_SIZE)); MAX_PEERS,
MAX_PEERS,
false));
final EthMessages ethMessages = new EthMessages(); final EthMessages ethMessages = new EthMessages();
final EthScheduler ethScheduler = final EthScheduler ethScheduler =
new DeterministicEthScheduler( new DeterministicEthScheduler(

@ -37,6 +37,7 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer; import java.util.function.Consumer;
import org.apache.tuweni.bytes.Bytes;
import org.junit.Test; import org.junit.Test;
/** /**
@ -166,6 +167,7 @@ public abstract class PeerMessageTaskTest<T>
Collections.emptyList(), Collections.emptyList(),
EthProtocolConfiguration.DEFAULT_MAX_MESSAGE_SIZE, EthProtocolConfiguration.DEFAULT_MAX_MESSAGE_SIZE,
TestClock.fixed(), TestClock.fixed(),
Collections.emptyList()); Collections.emptyList(),
Bytes.random(64));
} }
} }

@ -623,8 +623,13 @@ public abstract class AbstractBlockPropagationManagerTest {
() -> protocolSchedule.getByBlockHeader(blockchain.getChainHeadHeader()), () -> protocolSchedule.getByBlockHeader(blockchain.getChainHeadHeader()),
TestClock.fixed(), TestClock.fixed(),
metricsSystem, metricsSystem,
EthProtocolConfiguration.DEFAULT_MAX_MESSAGE_SIZE,
Collections.emptyList(),
Bytes.random(64),
25, 25,
EthProtocolConfiguration.DEFAULT_MAX_MESSAGE_SIZE), 25,
25,
false),
new EthMessages(), new EthMessages(),
ethScheduler); ethScheduler);
final BlockPropagationManager blockPropagationManager = final BlockPropagationManager blockPropagationManager =
@ -749,6 +754,7 @@ public abstract class AbstractBlockPropagationManagerTest {
return invocation.getArgument(0, Supplier.class).get(); return invocation.getArgument(0, Supplier.class).get();
} }
}); });
final EthContext ethContext = final EthContext ethContext =
new EthContext( new EthContext(
new EthPeers( new EthPeers(
@ -756,8 +762,13 @@ public abstract class AbstractBlockPropagationManagerTest {
() -> protocolSchedule.getByBlockHeader(blockchain.getChainHeadHeader()), () -> protocolSchedule.getByBlockHeader(blockchain.getChainHeadHeader()),
TestClock.fixed(), TestClock.fixed(),
metricsSystem, metricsSystem,
EthProtocolConfiguration.DEFAULT_MAX_MESSAGE_SIZE,
Collections.emptyList(),
Bytes.random(64),
25,
25,
25, 25,
EthProtocolConfiguration.DEFAULT_MAX_MESSAGE_SIZE), false),
new EthMessages(), new EthMessages(),
ethScheduler); ethScheduler);
final BlockPropagationManager blockPropagationManager = final BlockPropagationManager blockPropagationManager =

@ -145,7 +145,7 @@ public class TrailingPeerLimiterTest {
final List<EthPeer> disconnected = asList(disconnectedPeers); final List<EthPeer> disconnected = asList(disconnectedPeers);
for (final EthPeer peer : peers) { for (final EthPeer peer : peers) {
if (disconnected.contains(peer)) { if (disconnected.contains(peer)) {
verify(peer).disconnect(DisconnectReason.TOO_MANY_PEERS); verify(peer).disconnect(DisconnectReason.USELESS_PEER);
} else { } else {
verify(peer, never()).disconnect(any(DisconnectReason.class)); verify(peer, never()).disconnect(any(DisconnectReason.class));
} }

@ -54,12 +54,15 @@ import org.hyperledger.besu.ethereum.p2p.network.NetworkRunner;
import org.hyperledger.besu.ethereum.p2p.network.P2PNetwork; import org.hyperledger.besu.ethereum.p2p.network.P2PNetwork;
import org.hyperledger.besu.ethereum.p2p.peers.DefaultPeer; import org.hyperledger.besu.ethereum.p2p.peers.DefaultPeer;
import org.hyperledger.besu.ethereum.p2p.peers.Peer; import org.hyperledger.besu.ethereum.p2p.peers.Peer;
import org.hyperledger.besu.ethereum.p2p.rlpx.RlpxAgent;
import org.hyperledger.besu.ethereum.p2p.rlpx.connections.PeerConnection; import org.hyperledger.besu.ethereum.p2p.rlpx.connections.PeerConnection;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.messages.DisconnectMessage.DisconnectReason; import org.hyperledger.besu.ethereum.p2p.rlpx.wire.messages.DisconnectMessage.DisconnectReason;
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
import org.hyperledger.besu.evm.internal.EvmConfiguration; import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
import org.hyperledger.besu.plugin.data.EnodeURL;
import org.hyperledger.besu.plugin.services.MetricsSystem; import org.hyperledger.besu.plugin.services.MetricsSystem;
import org.hyperledger.besu.plugin.services.permissioning.NodeMessagePermissioningProvider;
import org.hyperledger.besu.testutil.TestClock; import org.hyperledger.besu.testutil.TestClock;
import java.io.Closeable; import java.io.Closeable;
@ -128,15 +131,26 @@ public class TestNode implements Closeable {
when(syncState.isInitialSyncPhaseDone()).thenReturn(true); when(syncState.isInitialSyncPhaseDone()).thenReturn(true);
final EthMessages ethMessages = new EthMessages(); final EthMessages ethMessages = new EthMessages();
final NodeMessagePermissioningProvider nmpp =
new NodeMessagePermissioningProvider() {
@Override
public boolean isMessagePermitted(final EnodeURL destinationEnode, final int code) {
return true;
}
};
final EthPeers ethPeers = final EthPeers ethPeers =
new EthPeers( new EthPeers(
EthProtocol.NAME, EthProtocol.NAME,
() -> protocolSchedule.getByBlockHeader(blockchain.getChainHeadHeader()), () -> protocolSchedule.getByBlockHeader(blockchain.getChainHeadHeader()),
TestClock.fixed(), TestClock.fixed(),
metricsSystem, metricsSystem,
EthProtocolConfiguration.DEFAULT_MAX_MESSAGE_SIZE,
Collections.singletonList(nmpp),
Bytes.random(64),
25,
25,
25, 25,
EthProtocolConfiguration.DEFAULT_MAX_MESSAGE_SIZE); false);
final EthScheduler scheduler = new EthScheduler(1, 1, 1, metricsSystem); final EthScheduler scheduler = new EthScheduler(1, 1, 1, metricsSystem);
final EthContext ethContext = new EthContext(ethPeers, ethMessages, scheduler); final EthContext ethContext = new EthContext(ethPeers, ethMessages, scheduler);
@ -183,10 +197,15 @@ public class TestNode implements Closeable {
.blockchain(blockchain) .blockchain(blockchain)
.blockNumberForks(Collections.emptyList()) .blockNumberForks(Collections.emptyList())
.timestampForks(Collections.emptyList()) .timestampForks(Collections.emptyList())
.allConnectionsSupplier(ethPeers::getAllConnections)
.allActiveConnectionsSupplier(ethPeers::getAllActiveConnections)
.build()) .build())
.metricsSystem(new NoOpMetricsSystem()) .metricsSystem(new NoOpMetricsSystem())
.build(); .build();
network = networkRunner.getNetwork(); network = networkRunner.getNetwork();
final RlpxAgent rlpxAgent = network.getRlpxAgent();
rlpxAgent.subscribeConnectRequest((p, d) -> true);
ethPeers.setRlpxAgent(rlpxAgent);
network.subscribeDisconnect( network.subscribeDisconnect(
(connection, reason, initiatedByPeer) -> disconnections.put(connection, reason)); (connection, reason, initiatedByPeer) -> disconnections.put(connection, reason));

@ -51,6 +51,8 @@ import org.hyperledger.besu.ethereum.mainnet.ProtocolSpecAdapters;
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
import org.hyperledger.besu.evm.internal.EvmConfiguration; import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
import org.hyperledger.besu.plugin.data.EnodeURL;
import org.hyperledger.besu.plugin.services.permissioning.NodeMessagePermissioningProvider;
import org.hyperledger.besu.testutil.TestClock; import org.hyperledger.besu.testutil.TestClock;
import java.math.BigInteger; import java.math.BigInteger;
@ -58,6 +60,7 @@ import java.util.Collections;
import java.util.Optional; import java.util.Optional;
import java.util.function.Function; import java.util.function.Function;
import org.apache.tuweni.bytes.Bytes;
import org.assertj.core.api.Condition; import org.assertj.core.api.Condition;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@ -95,14 +98,27 @@ public class TransactionPoolFactoryTest {
public void setup() { public void setup() {
when(blockchain.getBlockHashByNumber(anyLong())).thenReturn(Optional.of(mock(Hash.class))); when(blockchain.getBlockHashByNumber(anyLong())).thenReturn(Optional.of(mock(Hash.class)));
when(context.getBlockchain()).thenReturn(blockchain); when(context.getBlockchain()).thenReturn(blockchain);
final NodeMessagePermissioningProvider nmpp =
new NodeMessagePermissioningProvider() {
@Override
public boolean isMessagePermitted(final EnodeURL destinationEnode, final int code) {
return true;
}
};
ethPeers = ethPeers =
new EthPeers( new EthPeers(
"ETH", "ETH",
() -> protocolSpec, () -> protocolSpec,
TestClock.fixed(), TestClock.fixed(),
new NoOpMetricsSystem(), new NoOpMetricsSystem(),
EthProtocolConfiguration.DEFAULT_MAX_MESSAGE_SIZE,
Collections.singletonList(nmpp),
Bytes.random(64),
25,
25,
25, 25,
EthProtocolConfiguration.DEFAULT_MAX_MESSAGE_SIZE); false);
when(ethContext.getEthMessages()).thenReturn(ethMessages); when(ethContext.getEthMessages()).thenReturn(ethMessages);
when(ethContext.getEthPeers()).thenReturn(ethPeers); when(ethContext.getEthPeers()).thenReturn(ethPeers);

@ -26,6 +26,7 @@ import org.hyperledger.besu.ethereum.p2p.rlpx.wire.DefaultMessage;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.Message; import org.hyperledger.besu.ethereum.p2p.rlpx.wire.Message;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.MessageData; import org.hyperledger.besu.ethereum.p2p.rlpx.wire.MessageData;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.PeerInfo; import org.hyperledger.besu.ethereum.p2p.rlpx.wire.PeerInfo;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.ShouldConnectCallback;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.messages.DisconnectMessage.DisconnectReason; import org.hyperledger.besu.ethereum.p2p.rlpx.wire.messages.DisconnectMessage.DisconnectReason;
import org.hyperledger.besu.plugin.data.EnodeURL; import org.hyperledger.besu.plugin.data.EnodeURL;
import org.hyperledger.besu.util.Subscribers; import org.hyperledger.besu.util.Subscribers;
@ -168,6 +169,9 @@ public final class MockNetwork {
connectCallbacks.subscribe(callback); connectCallbacks.subscribe(callback);
} }
@Override
public void subscribeConnectRequest(final ShouldConnectCallback callback) {}
@Override @Override
public void subscribeDisconnect(final DisconnectCallback callback) { public void subscribeDisconnect(final DisconnectCallback callback) {
disconnectCallbacks.subscribe(callback); disconnectCallbacks.subscribe(callback);
@ -237,6 +241,8 @@ public final class MockNetwork {
private final Peer to; private final Peer to;
private final MockNetwork network; private final MockNetwork network;
private boolean statusSent;
private boolean statusReceived;
MockPeerConnection(final Peer source, final Peer target, final MockNetwork network) { MockPeerConnection(final Peer source, final Peer target, final MockNetwork network) {
from = source; from = source;
@ -308,5 +314,30 @@ public final class MockNetwork {
public InetSocketAddress getRemoteAddress() { public InetSocketAddress getRemoteAddress() {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@Override
public long getInitiatedAt() {
return 0;
}
@Override
public boolean inboundInitiated() {
return false;
}
@Override
public void setStatusSent() {
this.statusSent = true;
}
@Override
public void setStatusReceived() {
this.statusReceived = true;
}
@Override
public boolean getStatusExchanged() {
return statusSent && statusReceived;
}
} }
} }

@ -43,9 +43,9 @@ public class DiscoveryConfiguration {
bootnodes.stream().filter(e -> !e.isRunningDiscovery()).collect(Collectors.toList()); bootnodes.stream().filter(e -> !e.isRunningDiscovery()).collect(Collectors.toList());
if (invalidEnodes.size() > 0) { if (invalidEnodes.size() > 0) {
String invalidBootnodes = final String invalidBootnodes =
invalidEnodes.stream().map(EnodeURL::toString).collect(Collectors.joining(",")); invalidEnodes.stream().map(EnodeURL::toString).collect(Collectors.joining(","));
String errorMsg = final String errorMsg =
"Bootnodes must have discovery enabled. Invalid bootnodes: " + invalidBootnodes + "."; "Bootnodes must have discovery enabled. Invalid bootnodes: " + invalidBootnodes + ".";
throw new IllegalArgumentException(errorMsg); throw new IllegalArgumentException(errorMsg);
} }

@ -22,12 +22,14 @@ import java.util.Optional;
public class NetworkingConfiguration { public class NetworkingConfiguration {
public static final int DEFAULT_INITIATE_CONNECTIONS_FREQUENCY_SEC = 30; public static final int DEFAULT_INITIATE_CONNECTIONS_FREQUENCY_SEC = 30;
public static final int DEFAULT_CHECK_MAINTAINED_CONNECTIONS_FREQUENCY_SEC = 60; public static final int DEFAULT_CHECK_MAINTAINED_CONNECTIONS_FREQUENCY_SEC = 60;
public static final int DEFAULT_PEER_LOWER_BOUND = 25;
private DiscoveryConfiguration discovery = new DiscoveryConfiguration(); private DiscoveryConfiguration discovery = new DiscoveryConfiguration();
private RlpxConfiguration rlpx = new RlpxConfiguration(); private RlpxConfiguration rlpx = new RlpxConfiguration();
private int initiateConnectionsFrequencySec = DEFAULT_INITIATE_CONNECTIONS_FREQUENCY_SEC; private int initiateConnectionsFrequencySec = DEFAULT_INITIATE_CONNECTIONS_FREQUENCY_SEC;
private int checkMaintainedConnectionsFrequencySec = private int checkMaintainedConnectionsFrequencySec =
DEFAULT_CHECK_MAINTAINED_CONNECTIONS_FREQUENCY_SEC; DEFAULT_CHECK_MAINTAINED_CONNECTIONS_FREQUENCY_SEC;
private Integer peerLowerBound = DEFAULT_PEER_LOWER_BOUND;
private Optional<String> dnsDiscoveryServerOverride = Optional.empty(); private Optional<String> dnsDiscoveryServerOverride = Optional.empty();
public static NetworkingConfiguration create() { public static NetworkingConfiguration create() {
@ -84,6 +86,16 @@ public class NetworkingConfiguration {
return this; return this;
} }
public Integer getPeerLowerBound() {
return peerLowerBound;
}
public NetworkingConfiguration setPeerLowerBound(final Integer peerLowerBoundConfig) {
checkArgument(peerLowerBoundConfig > 0);
this.peerLowerBound = peerLowerBoundConfig;
return this;
}
@Override @Override
public boolean equals(final Object o) { public boolean equals(final Object o) {
if (o == this) { if (o == this) {

@ -14,8 +14,6 @@
*/ */
package org.hyperledger.besu.ethereum.p2p.config; package org.hyperledger.besu.ethereum.p2p.config;
import static com.google.common.base.Preconditions.checkState;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.SubProtocol; import org.hyperledger.besu.ethereum.p2p.rlpx.wire.SubProtocol;
import org.hyperledger.besu.util.NetworkUtility; import org.hyperledger.besu.util.NetworkUtility;
@ -29,10 +27,6 @@ public class RlpxConfiguration {
private String clientId = "TestClient/1.0.0"; private String clientId = "TestClient/1.0.0";
private String bindHost = NetworkUtility.INADDR_ANY; private String bindHost = NetworkUtility.INADDR_ANY;
private int bindPort = 30303; private int bindPort = 30303;
private int peerUpperBound = 100;
private int peerLowerBound = 64;
private boolean limitRemoteWireConnectionsEnabled = false;
private float fractionRemoteWireConnectionsAllowed = DEFAULT_FRACTION_REMOTE_CONNECTIONS_ALLOWED;
private List<SubProtocol> supportedProtocols = Collections.emptyList(); private List<SubProtocol> supportedProtocols = Collections.emptyList();
public static RlpxConfiguration create() { public static RlpxConfiguration create() {
@ -71,15 +65,6 @@ public class RlpxConfiguration {
return this; return this;
} }
public RlpxConfiguration setPeerUpperBound(final int peers) {
peerUpperBound = peers;
return this;
}
public int getPeerUpperBound() {
return peerUpperBound;
}
public String getClientId() { public String getClientId() {
return clientId; return clientId;
} }
@ -89,29 +74,6 @@ public class RlpxConfiguration {
return this; return this;
} }
public RlpxConfiguration setLimitRemoteWireConnectionsEnabled(
final boolean limitRemoteWireConnectionsEnabled) {
this.limitRemoteWireConnectionsEnabled = limitRemoteWireConnectionsEnabled;
return this;
}
public RlpxConfiguration setFractionRemoteWireConnectionsAllowed(
final float fractionRemoteWireConnectionsAllowed) {
checkState(
fractionRemoteWireConnectionsAllowed >= 0.0 && fractionRemoteWireConnectionsAllowed <= 1.0,
"Fraction of remote connections allowed must be between 0.0 and 1.0 (inclusive).");
this.fractionRemoteWireConnectionsAllowed = fractionRemoteWireConnectionsAllowed;
return this;
}
public int getMaxRemotelyInitiatedConnections() {
if (!limitRemoteWireConnectionsEnabled) {
return peerUpperBound;
}
return (int) Math.floor(peerUpperBound * fractionRemoteWireConnectionsAllowed);
}
@Override @Override
public boolean equals(final Object o) { public boolean equals(final Object o) {
if (this == o) { if (this == o) {
@ -137,13 +99,4 @@ public class RlpxConfiguration {
sb.append('}'); sb.append('}');
return sb.toString(); return sb.toString();
} }
public RlpxConfiguration setPeerLowerBound(final int peers) {
peerLowerBound = peers;
return this;
}
public int getPeerLowerBound() {
return peerLowerBound;
}
} }

@ -132,6 +132,7 @@ public class DiscoveryPeer extends DefaultPeer {
return endpoint; return endpoint;
} }
@Override
public Optional<NodeRecord> getNodeRecord() { public Optional<NodeRecord> getNodeRecord() {
return Optional.ofNullable(nodeRecord); return Optional.ofNullable(nodeRecord);
} }
@ -141,10 +142,16 @@ public class DiscoveryPeer extends DefaultPeer {
this.forkId = ForkId.fromRawForkId(nodeRecord.get("eth")); this.forkId = ForkId.fromRawForkId(nodeRecord.get("eth"));
} }
@Override
public Optional<ForkId> getForkId() { public Optional<ForkId> getForkId() {
return this.forkId; return this.forkId;
} }
@Override
public void setForkId(final ForkId forkId) {
this.forkId = Optional.ofNullable(forkId);
}
public boolean discoveryEndpointMatches(final DiscoveryPeer peer) { public boolean discoveryEndpointMatches(final DiscoveryPeer peer) {
return peer.getEndpoint().getHost().equals(endpoint.getHost()) return peer.getEndpoint().getHost().equals(endpoint.getHost())
&& peer.getEndpoint().getUdpPort() == endpoint.getUdpPort(); && peer.getEndpoint().getUdpPort() == endpoint.getUdpPort();

@ -43,6 +43,7 @@ import org.hyperledger.besu.ethereum.p2p.rlpx.RlpxAgent;
import org.hyperledger.besu.ethereum.p2p.rlpx.connections.PeerConnection; import org.hyperledger.besu.ethereum.p2p.rlpx.connections.PeerConnection;
import org.hyperledger.besu.ethereum.p2p.rlpx.connections.netty.TLSConfiguration; import org.hyperledger.besu.ethereum.p2p.rlpx.connections.netty.TLSConfiguration;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.Capability; import org.hyperledger.besu.ethereum.p2p.rlpx.wire.Capability;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.ShouldConnectCallback;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.messages.DisconnectMessage.DisconnectReason; import org.hyperledger.besu.ethereum.p2p.rlpx.wire.messages.DisconnectMessage.DisconnectReason;
import org.hyperledger.besu.ethereum.storage.StorageProvider; import org.hyperledger.besu.ethereum.storage.StorageProvider;
import org.hyperledger.besu.nat.NatMethod; import org.hyperledger.besu.nat.NatMethod;
@ -68,6 +69,7 @@ import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
@ -183,7 +185,7 @@ public class DefaultP2PNetwork implements P2PNetwork {
this.peerPermissions = peerPermissions; this.peerPermissions = peerPermissions;
// set the requirement here that the number of peers be greater than the lower bound // set the requirement here that the number of peers be greater than the lower bound
final int peerLowerBound = config.getRlpx().getPeerLowerBound(); final int peerLowerBound = rlpxAgent.getPeerLowerBound();
LOG.debug("setting peerLowerBound {}", peerLowerBound); LOG.debug("setting peerLowerBound {}", peerLowerBound);
peerDiscoveryAgent.addPeerRequirement(() -> rlpxAgent.getConnectionCount() >= peerLowerBound); peerDiscoveryAgent.addPeerRequirement(() -> rlpxAgent.getConnectionCount() >= peerLowerBound);
subscribeDisconnect(reputationManager); subscribeDisconnect(reputationManager);
@ -302,6 +304,11 @@ public class DefaultP2PNetwork implements P2PNetwork {
} }
} }
@Override
public RlpxAgent getRlpxAgent() {
return rlpxAgent;
}
@Override @Override
public boolean addMaintainedConnectionPeer(final Peer peer) { public boolean addMaintainedConnectionPeer(final Peer peer) {
if (localNode.isReady() if (localNode.isReady()
@ -356,11 +363,15 @@ public class DefaultP2PNetwork implements P2PNetwork {
if (!localNode.isReady()) { if (!localNode.isReady()) {
return; return;
} }
final EnodeURL localEnodeURL = localNode.getPeer().getEnodeURL(); final List<Bytes> doNotConnectTo =
rlpxAgent
.streamActiveConnections()
.map(c -> c.getPeer().getId())
.collect(Collectors.toList());
doNotConnectTo.add(localNode.getPeer().getEnodeURL().getNodeId());
maintainedPeers maintainedPeers
.streamPeers() .streamPeers()
.filter(peer -> !peer.getEnodeURL().getNodeId().equals(localEnodeURL.getNodeId())) .filter(p -> !doNotConnectTo.contains(p.getId()))
.filter(p -> !rlpxAgent.getPeerConnection(p).isPresent())
.forEach(rlpxAgent::connect); .forEach(rlpxAgent::connect);
} }
@ -379,6 +390,11 @@ public class DefaultP2PNetwork implements P2PNetwork {
return rlpxAgent.streamConnections().collect(Collectors.toList()); return rlpxAgent.streamConnections().collect(Collectors.toList());
} }
@Override
public int getPeerCount() {
return getRlpxAgent().getConnectionCount();
}
@Override @Override
public Stream<DiscoveryPeer> streamDiscoveredPeers() { public Stream<DiscoveryPeer> streamDiscoveredPeers() {
return peerDiscoveryAgent.streamDiscoveredPeers(); return peerDiscoveryAgent.streamDiscoveredPeers();
@ -399,6 +415,12 @@ public class DefaultP2PNetwork implements P2PNetwork {
rlpxAgent.subscribeConnect(callback); rlpxAgent.subscribeConnect(callback);
} }
@Override
public void subscribeConnectRequest(final ShouldConnectCallback callback) {
rlpxAgent.subscribeConnectRequest(callback);
}
;
@Override @Override
public void subscribeDisconnect(final DisconnectCallback callback) { public void subscribeDisconnect(final DisconnectCallback callback) {
rlpxAgent.subscribeDisconnect(callback); rlpxAgent.subscribeDisconnect(callback);
@ -474,8 +496,6 @@ public class DefaultP2PNetwork implements P2PNetwork {
private PeerPermissions peerPermissions = PeerPermissions.noop(); private PeerPermissions peerPermissions = PeerPermissions.noop();
private NatService natService = new NatService(Optional.empty()); private NatService natService = new NatService(Optional.empty());
private boolean randomPeerPriority;
private MetricsSystem metricsSystem; private MetricsSystem metricsSystem;
private StorageProvider storageProvider; private StorageProvider storageProvider;
private Optional<TLSConfiguration> p2pTLSConfiguration = Optional.empty(); private Optional<TLSConfiguration> p2pTLSConfiguration = Optional.empty();
@ -483,6 +503,9 @@ public class DefaultP2PNetwork implements P2PNetwork {
private List<Long> blockNumberForks; private List<Long> blockNumberForks;
private List<Long> timestampForks; private List<Long> timestampForks;
private boolean legacyForkIdEnabled = false; private boolean legacyForkIdEnabled = false;
private Supplier<Stream<PeerConnection>> allConnectionsSupplier;
private Supplier<Stream<PeerConnection>> allActiveConnectionsSupplier;
private int peersLowerBound;
public P2PNetwork build() { public P2PNetwork build() {
validate(); validate();
@ -526,6 +549,8 @@ public class DefaultP2PNetwork implements P2PNetwork {
checkState(peerDiscoveryAgent != null || vertx != null, "Vertx must be set."); checkState(peerDiscoveryAgent != null || vertx != null, "Vertx must be set.");
checkState(blockNumberForks != null, "BlockNumberForks must be set."); checkState(blockNumberForks != null, "BlockNumberForks must be set.");
checkState(timestampForks != null, "TimestampForks must be set."); checkState(timestampForks != null, "TimestampForks must be set.");
checkState(allConnectionsSupplier != null, "Supplier must be set.");
checkState(allActiveConnectionsSupplier != null, "Supplier must be set.");
} }
private PeerDiscoveryAgent createDiscoveryAgent() { private PeerDiscoveryAgent createDiscoveryAgent() {
@ -546,6 +571,7 @@ public class DefaultP2PNetwork implements P2PNetwork {
private RlpxAgent createRlpxAgent( private RlpxAgent createRlpxAgent(
final LocalNode localNode, final PeerPrivileges peerPrivileges) { final LocalNode localNode, final PeerPrivileges peerPrivileges) {
return RlpxAgent.builder() return RlpxAgent.builder()
.nodeKey(nodeKey) .nodeKey(nodeKey)
.config(config.getRlpx()) .config(config.getRlpx())
@ -553,8 +579,10 @@ public class DefaultP2PNetwork implements P2PNetwork {
.peerPrivileges(peerPrivileges) .peerPrivileges(peerPrivileges)
.localNode(localNode) .localNode(localNode)
.metricsSystem(metricsSystem) .metricsSystem(metricsSystem)
.randomPeerPriority(randomPeerPriority)
.p2pTLSConfiguration(p2pTLSConfiguration) .p2pTLSConfiguration(p2pTLSConfiguration)
.allConnectionsSupplier(allConnectionsSupplier)
.allActiveConnectionsSupplier(allActiveConnectionsSupplier)
.peersLowerBound(peersLowerBound)
.build(); .build();
} }
@ -570,11 +598,6 @@ public class DefaultP2PNetwork implements P2PNetwork {
return this; return this;
} }
public Builder randomPeerPriority(final boolean randomPeerPriority) {
this.randomPeerPriority = randomPeerPriority;
return this;
}
public Builder vertx(final Vertx vertx) { public Builder vertx(final Vertx vertx) {
checkNotNull(vertx); checkNotNull(vertx);
this.vertx = vertx; this.vertx = vertx;
@ -662,5 +685,22 @@ public class DefaultP2PNetwork implements P2PNetwork {
this.legacyForkIdEnabled = legacyForkIdEnabled; this.legacyForkIdEnabled = legacyForkIdEnabled;
return this; return this;
} }
public Builder allConnectionsSupplier(
final Supplier<Stream<PeerConnection>> allConnectionsSupplier) {
this.allConnectionsSupplier = allConnectionsSupplier;
return this;
}
public Builder allActiveConnectionsSupplier(
final Supplier<Stream<PeerConnection>> allActiveConnectionsSupplier) {
this.allActiveConnectionsSupplier = allActiveConnectionsSupplier;
return this;
}
public Builder peersLowerBound(final int peersLowerBound) {
this.peersLowerBound = peersLowerBound;
return this;
}
} }
} }

@ -14,6 +14,7 @@
*/ */
package org.hyperledger.besu.ethereum.p2p.network; package org.hyperledger.besu.ethereum.p2p.network;
import org.hyperledger.besu.ethereum.p2p.rlpx.RlpxAgent;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.Capability; import org.hyperledger.besu.ethereum.p2p.rlpx.wire.Capability;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.SubProtocol; import org.hyperledger.besu.ethereum.p2p.rlpx.wire.SubProtocol;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.messages.DisconnectMessage.DisconnectReason; import org.hyperledger.besu.ethereum.p2p.rlpx.wire.messages.DisconnectMessage.DisconnectReason;
@ -154,6 +155,9 @@ public class NetworkRunner implements AutoCloseable {
protocolManager.handleNewConnection(connection); protocolManager.handleNewConnection(connection);
}); });
network.subscribeConnectRequest(
(peer, incoming) -> protocolManager.shouldConnect(peer, incoming));
network.subscribeDisconnect( network.subscribeDisconnect(
(connection, disconnectReason, initiatedByPeer) -> { (connection, disconnectReason, initiatedByPeer) -> {
if (Collections.disjoint( if (Collections.disjoint(
@ -170,6 +174,10 @@ public class NetworkRunner implements AutoCloseable {
stop(); stop();
} }
public RlpxAgent getRlpxAgent() {
return network.getRlpxAgent();
}
public static class Builder { public static class Builder {
private NetworkBuilder networkProvider; private NetworkBuilder networkProvider;
List<ProtocolManager> protocolManagers = new ArrayList<>(); List<ProtocolManager> protocolManagers = new ArrayList<>();

@ -22,6 +22,7 @@ import org.hyperledger.besu.ethereum.p2p.rlpx.DisconnectCallback;
import org.hyperledger.besu.ethereum.p2p.rlpx.MessageCallback; import org.hyperledger.besu.ethereum.p2p.rlpx.MessageCallback;
import org.hyperledger.besu.ethereum.p2p.rlpx.connections.PeerConnection; import org.hyperledger.besu.ethereum.p2p.rlpx.connections.PeerConnection;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.Capability; import org.hyperledger.besu.ethereum.p2p.rlpx.wire.Capability;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.ShouldConnectCallback;
import org.hyperledger.besu.plugin.data.EnodeURL; import org.hyperledger.besu.plugin.data.EnodeURL;
import java.io.IOException; import java.io.IOException;
@ -52,6 +53,9 @@ public class NoopP2PNetwork implements P2PNetwork {
@Override @Override
public void subscribeConnect(final ConnectCallback callback) {} public void subscribeConnect(final ConnectCallback callback) {}
@Override
public void subscribeConnectRequest(final ShouldConnectCallback callback) {}
@Override @Override
public void subscribeDisconnect(final DisconnectCallback callback) {} public void subscribeDisconnect(final DisconnectCallback callback) {}

@ -19,9 +19,11 @@ import org.hyperledger.besu.ethereum.p2p.peers.Peer;
import org.hyperledger.besu.ethereum.p2p.rlpx.ConnectCallback; import org.hyperledger.besu.ethereum.p2p.rlpx.ConnectCallback;
import org.hyperledger.besu.ethereum.p2p.rlpx.DisconnectCallback; import org.hyperledger.besu.ethereum.p2p.rlpx.DisconnectCallback;
import org.hyperledger.besu.ethereum.p2p.rlpx.MessageCallback; import org.hyperledger.besu.ethereum.p2p.rlpx.MessageCallback;
import org.hyperledger.besu.ethereum.p2p.rlpx.RlpxAgent;
import org.hyperledger.besu.ethereum.p2p.rlpx.connections.PeerConnection; import org.hyperledger.besu.ethereum.p2p.rlpx.connections.PeerConnection;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.Capability; import org.hyperledger.besu.ethereum.p2p.rlpx.wire.Capability;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.Message; import org.hyperledger.besu.ethereum.p2p.rlpx.wire.Message;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.ShouldConnectCallback;
import org.hyperledger.besu.plugin.data.EnodeURL; import org.hyperledger.besu.plugin.data.EnodeURL;
import java.io.Closeable; import java.io.Closeable;
@ -83,8 +85,9 @@ public interface P2PNetwork extends Closeable {
* *
* @param callback The callback to invoke when a new connection is established * @param callback The callback to invoke when a new connection is established
*/ */
void subscribeConnect(ConnectCallback callback); void subscribeConnect(final ConnectCallback callback);
void subscribeConnectRequest(final ShouldConnectCallback callback);
/** /**
* Subscribe a {@link Consumer} to all incoming new Peer disconnect events. * Subscribe a {@link Consumer} to all incoming new Peer disconnect events.
* *
@ -108,7 +111,7 @@ public interface P2PNetwork extends Closeable {
* disconnected even if it is not in the maintained peer list. See {@link * disconnected even if it is not in the maintained peer list. See {@link
* #addMaintainedConnectionPeer(Peer)} for details on the maintained peer list. * #addMaintainedConnectionPeer(Peer)} for details on the maintained peer list.
* *
* @param peer The peer to which connections are not longer required * @param peer The peer to which connections are no longer required
* @return boolean representing whether the peer was removed from the maintained peer list * @return boolean representing whether the peer was removed from the maintained peer list
*/ */
boolean removeMaintainedConnectionPeer(final Peer peer); boolean removeMaintainedConnectionPeer(final Peer peer);
@ -149,4 +152,9 @@ public interface P2PNetwork extends Closeable {
Optional<EnodeURL> getLocalEnode(); Optional<EnodeURL> getLocalEnode();
void updateNodeRecord(); void updateNodeRecord();
default RlpxAgent getRlpxAgent() {
return null;
}
;
} }

@ -14,6 +14,7 @@
*/ */
package org.hyperledger.besu.ethereum.p2p.network; package org.hyperledger.besu.ethereum.p2p.network;
import org.hyperledger.besu.ethereum.p2p.peers.Peer;
import org.hyperledger.besu.ethereum.p2p.rlpx.connections.PeerConnection; import org.hyperledger.besu.ethereum.p2p.rlpx.connections.PeerConnection;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.Capability; import org.hyperledger.besu.ethereum.p2p.rlpx.wire.Capability;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.Message; import org.hyperledger.besu.ethereum.p2p.rlpx.wire.Message;
@ -58,6 +59,14 @@ public interface ProtocolManager extends AutoCloseable {
*/ */
void handleNewConnection(PeerConnection peerConnection); void handleNewConnection(PeerConnection peerConnection);
/**
* Call this to find out whether we should try to connect to a certain peer
*
* @param peer the peer that we are trying to connect to
* @param incoming true if the connection is incoming
* @return true, if the ProtocolManager wants to connect to the peer, false otherwise
*/
boolean shouldConnect(Peer peer, final boolean incoming);
/** /**
* Handles peer disconnects. * Handles peer disconnects.
* *

@ -14,15 +14,18 @@
*/ */
package org.hyperledger.besu.ethereum.p2p.peers; package org.hyperledger.besu.ethereum.p2p.peers;
import org.hyperledger.besu.ethereum.forkid.ForkId;
import org.hyperledger.besu.plugin.data.EnodeURL; import org.hyperledger.besu.plugin.data.EnodeURL;
import java.net.URI; import java.net.URI;
import java.util.Objects; import java.util.Objects;
import java.util.Optional;
/** The default, basic representation of an Ethereum {@link Peer}. */ /** The default, basic representation of an Ethereum {@link Peer}. */
public class DefaultPeer extends DefaultPeerId implements Peer { public class DefaultPeer extends DefaultPeerId implements Peer {
private final EnodeURL enodeURL; private final EnodeURL enodeURL;
private ForkId forkId;
protected DefaultPeer(final EnodeURL enodeURL) { protected DefaultPeer(final EnodeURL enodeURL) {
super(enodeURL.getNodeId()); super(enodeURL.getNodeId());
@ -60,6 +63,16 @@ public class DefaultPeer extends DefaultPeerId implements Peer {
return enodeURL; return enodeURL;
} }
@Override
public Optional<ForkId> getForkId() {
return Optional.ofNullable(forkId);
}
@Override
public void setForkId(final ForkId forkId) {
this.forkId = forkId;
}
@Override @Override
public boolean equals(final Object obj) { public boolean equals(final Object obj) {
if (obj == null) { if (obj == null) {

@ -15,9 +15,13 @@
package org.hyperledger.besu.ethereum.p2p.peers; package org.hyperledger.besu.ethereum.p2p.peers;
import org.hyperledger.besu.crypto.SecureRandomProvider; import org.hyperledger.besu.crypto.SecureRandomProvider;
import org.hyperledger.besu.ethereum.forkid.ForkId;
import org.hyperledger.besu.plugin.data.EnodeURL; import org.hyperledger.besu.plugin.data.EnodeURL;
import java.util.Optional;
import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes;
import org.ethereum.beacon.discovery.schema.NodeRecord;
public interface Peer extends PeerId { public interface Peer extends PeerId {
@ -47,4 +51,29 @@ public interface Peer extends PeerId {
default String getEnodeURLString() { default String getEnodeURLString() {
return this.getEnodeURL().toString(); return this.getEnodeURL().toString();
} }
/**
* Returns the node record
*
* @return The node record wrapped in an Optional
*/
default Optional<NodeRecord> getNodeRecord() {
return Optional.empty();
}
/**
* Returns the fork id
*
* @return The fork id wrapped in an Optional
*/
default Optional<ForkId> getForkId() {
return Optional.empty();
}
/**
* Sets the fork id
*
* @param forkId The fork id
*/
void setForkId(ForkId forkId);
} }

@ -16,9 +16,7 @@ package org.hyperledger.besu.ethereum.p2p.rlpx;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState; import static com.google.common.base.Preconditions.checkState;
import static java.util.Objects.isNull;
import org.hyperledger.besu.crypto.SECPPublicKey;
import org.hyperledger.besu.cryptoservices.NodeKey; import org.hyperledger.besu.cryptoservices.NodeKey;
import org.hyperledger.besu.ethereum.p2p.config.RlpxConfiguration; import org.hyperledger.besu.ethereum.p2p.config.RlpxConfiguration;
import org.hyperledger.besu.ethereum.p2p.discovery.DiscoveryPeer; import org.hyperledger.besu.ethereum.p2p.discovery.DiscoveryPeer;
@ -30,31 +28,33 @@ import org.hyperledger.besu.ethereum.p2p.rlpx.connections.ConnectionInitializer;
import org.hyperledger.besu.ethereum.p2p.rlpx.connections.PeerConnection; import org.hyperledger.besu.ethereum.p2p.rlpx.connections.PeerConnection;
import org.hyperledger.besu.ethereum.p2p.rlpx.connections.PeerConnectionEvents; import org.hyperledger.besu.ethereum.p2p.rlpx.connections.PeerConnectionEvents;
import org.hyperledger.besu.ethereum.p2p.rlpx.connections.PeerRlpxPermissions; import org.hyperledger.besu.ethereum.p2p.rlpx.connections.PeerRlpxPermissions;
import org.hyperledger.besu.ethereum.p2p.rlpx.connections.RlpxConnection;
import org.hyperledger.besu.ethereum.p2p.rlpx.connections.netty.NettyConnectionInitializer; import org.hyperledger.besu.ethereum.p2p.rlpx.connections.netty.NettyConnectionInitializer;
import org.hyperledger.besu.ethereum.p2p.rlpx.connections.netty.NettyTLSConnectionInitializer; import org.hyperledger.besu.ethereum.p2p.rlpx.connections.netty.NettyTLSConnectionInitializer;
import org.hyperledger.besu.ethereum.p2p.rlpx.connections.netty.TLSConfiguration; import org.hyperledger.besu.ethereum.p2p.rlpx.connections.netty.TLSConfiguration;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.Capability; import org.hyperledger.besu.ethereum.p2p.rlpx.wire.Capability;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.ShouldConnectCallback;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.messages.DisconnectMessage.DisconnectReason; import org.hyperledger.besu.ethereum.p2p.rlpx.wire.messages.DisconnectMessage.DisconnectReason;
import org.hyperledger.besu.metrics.BesuMetricCategory;
import org.hyperledger.besu.plugin.data.EnodeURL; import org.hyperledger.besu.plugin.data.EnodeURL;
import org.hyperledger.besu.plugin.services.MetricsSystem; import org.hyperledger.besu.plugin.services.MetricsSystem;
import org.hyperledger.besu.plugin.services.metrics.Counter;
import org.hyperledger.besu.util.Subscribers; import org.hyperledger.besu.util.Subscribers;
import java.time.Duration;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference; import java.util.function.Supplier;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -65,23 +65,20 @@ public class RlpxAgent {
private final PeerConnectionEvents connectionEvents; private final PeerConnectionEvents connectionEvents;
private final ConnectionInitializer connectionInitializer; private final ConnectionInitializer connectionInitializer;
private final Subscribers<ConnectCallback> connectSubscribers = Subscribers.create(); private final Subscribers<ConnectCallback> connectSubscribers = Subscribers.create();
private final List<ShouldConnectCallback> connectRequestSubscribers = new ArrayList<>();
private final PeerRlpxPermissions peerPermissions; private final PeerRlpxPermissions peerPermissions;
private final PeerPrivileges peerPrivileges; private final PeerPrivileges peerPrivileges;
private final int upperBoundConnections;
private final int lowerBoundConnections;
private final boolean randomPeerPriority;
private final int maxRemotelyInitiatedConnections;
// xor'ing with this mask will allow us to randomly let new peers connect
// without allowing the counterparty to play nodeId farming games
private final Bytes nodeIdMask = Bytes.random(SECPPublicKey.BYTE_LENGTH);
final Map<Bytes, RlpxConnection> connectionsById = new ConcurrentHashMap<>();
private final AtomicBoolean started = new AtomicBoolean(false); private final AtomicBoolean started = new AtomicBoolean(false);
private final AtomicBoolean stopped = new AtomicBoolean(false); private final AtomicBoolean stopped = new AtomicBoolean(false);
private final int lowerBound;
private final Counter connectedPeersCounter; private final Supplier<Stream<PeerConnection>> allConnectionsSupplier;
private final Supplier<Stream<PeerConnection>> allActiveConnectionsSupplier;
private final Cache<Bytes, CompletableFuture<PeerConnection>> peersConnectingCache =
CacheBuilder.newBuilder()
.expireAfterWrite(
Duration.ofSeconds(30L)) // we will at most try to connect every 30 seconds
.concurrencyLevel(1)
.build();
private RlpxAgent( private RlpxAgent(
final LocalNode localNode, final LocalNode localNode,
@ -89,32 +86,17 @@ public class RlpxAgent {
final ConnectionInitializer connectionInitializer, final ConnectionInitializer connectionInitializer,
final PeerRlpxPermissions peerPermissions, final PeerRlpxPermissions peerPermissions,
final PeerPrivileges peerPrivileges, final PeerPrivileges peerPrivileges,
final int upperBoundConnections, final int peersLowerBound,
final int lowerBoundConnections, final Supplier<Stream<PeerConnection>> allConnectionsSupplier,
final int maxRemotelyInitiatedConnections, final Supplier<Stream<PeerConnection>> allActiveConnectionsSupplier) {
final boolean randomPeerPriority,
final MetricsSystem metricsSystem) {
this.localNode = localNode; this.localNode = localNode;
this.connectionEvents = connectionEvents; this.connectionEvents = connectionEvents;
this.connectionInitializer = connectionInitializer; this.connectionInitializer = connectionInitializer;
this.peerPermissions = peerPermissions; this.peerPermissions = peerPermissions;
this.peerPrivileges = peerPrivileges; this.peerPrivileges = peerPrivileges;
this.upperBoundConnections = upperBoundConnections; this.lowerBound = peersLowerBound;
this.lowerBoundConnections = lowerBoundConnections; this.allConnectionsSupplier = allConnectionsSupplier;
this.randomPeerPriority = randomPeerPriority; this.allActiveConnectionsSupplier = allActiveConnectionsSupplier;
this.maxRemotelyInitiatedConnections =
Math.min(upperBoundConnections, maxRemotelyInitiatedConnections);
// Setup metrics
connectedPeersCounter =
metricsSystem.createCounter(
BesuMetricCategory.PEERS, "connected_total", "Total number of peers connected");
metricsSystem.createIntegerGauge(
BesuMetricCategory.ETHEREUM,
"peer_limit",
"The maximum number of peers this node allows to connect",
() -> upperBoundConnections);
} }
public static Builder builder() { public static Builder builder() {
@ -156,50 +138,58 @@ public class RlpxAgent {
return connectionInitializer.stop(); return connectionInitializer.stop();
} }
public Stream<? extends PeerConnection> streamConnections() { public Stream<PeerConnection> streamConnections() {
return connectionsById.values().stream() try {
.filter(RlpxConnection::isActive) return allConnectionsSupplier.get();
.map(RlpxConnection::getPeerConnection); } catch (final Exception e) {
throw new RuntimeException(e);
}
}
public Stream<PeerConnection> streamActiveConnections() {
try {
return allActiveConnectionsSupplier.get();
} catch (final Exception e) {
throw new RuntimeException(e);
}
} }
public int getConnectionCount() { public int getConnectionCount() {
return Math.toIntExact(streamConnections().count()); try {
return (int) allActiveConnectionsSupplier.get().count();
} catch (final Exception e) {
throw new RuntimeException(e);
}
} }
public void connect(final Stream<? extends Peer> peerStream) { public void connect(final Stream<? extends Peer> peerStream) {
if (!localNode.isReady()) { if (!localNode.isReady()) {
return; return;
} }
peerStream peerStream.forEach(this::connect);
.takeWhile(peer -> Math.max(0, lowerBoundConnections - getConnectionCount()) > 0)
.filter(peer -> !connectionsById.containsKey(peer.getId()))
.filter(peer -> peer.getEnodeURL().isListening())
.filter(peerPermissions::allowNewOutboundConnectionTo)
.forEach(this::connect);
}
private String logConnectionsByIdToString() {
final String connectionsList =
connectionsById.values().stream()
.map(RlpxConnection::toString)
.collect(Collectors.joining(",\n"));
return connectionsById.size() + " ConnectionsById {\n" + connectionsList + "}";
} }
public void disconnect(final Bytes peerId, final DisconnectReason reason) { public void disconnect(final Bytes peerId, final DisconnectReason reason) {
final RlpxConnection connection = connectionsById.remove(peerId); try {
if (connection != null) { allActiveConnectionsSupplier
connection.disconnect(reason); .get()
.filter(c -> c.getPeer().getId().equals(peerId))
.forEach(c -> c.disconnect(reason));
final CompletableFuture<PeerConnection> peerConnectionCompletableFuture =
getMapOfCompletableFutures().get(peerId);
if (peerConnectionCompletableFuture != null) {
if (!peerConnectionCompletableFuture.isDone()) {
peerConnectionCompletableFuture.cancel(true);
} else if (!peerConnectionCompletableFuture.isCompletedExceptionally()
&& !peerConnectionCompletableFuture.isCancelled()) {
peerConnectionCompletableFuture.get().disconnect(reason);
}
}
} catch (final Exception e) {
throw new RuntimeException(e);
} }
} }
public Optional<CompletableFuture<PeerConnection>> getPeerConnection(final Peer peer) {
final RlpxConnection connection = connectionsById.get(peer.getId());
return Optional.ofNullable(connection)
.filter(conn -> !conn.isFailedOrDisconnected())
.map(RlpxConnection::getFuture);
}
/** /**
* Connect to the peer * Connect to the peer
* *
@ -224,82 +214,55 @@ public class RlpxAgent {
return CompletableFuture.failedFuture((new IllegalArgumentException(errorMsg))); return CompletableFuture.failedFuture((new IllegalArgumentException(errorMsg)));
} }
// Shortcut checks if we're already connected
final Optional<CompletableFuture<PeerConnection>> peerConnection = getPeerConnection(peer);
if (peerConnection.isPresent()) {
return peerConnection.get();
}
// Check max peers
if (!peerPrivileges.canExceedConnectionLimits(peer)
&& getConnectionCount() >= upperBoundConnections) {
final String errorMsg =
"Max peer connections established ("
+ upperBoundConnections
+ "). Cannot connect to peer: "
+ peer;
return CompletableFuture.failedFuture(new IllegalStateException(errorMsg));
}
// Check permissions // Check permissions
if (!peerPermissions.allowNewOutboundConnectionTo(peer)) { if (!peerPermissions.allowNewOutboundConnectionTo(peer)) {
return CompletableFuture.failedFuture(peerPermissions.newOutboundConnectionException(peer)); return CompletableFuture.failedFuture(peerPermissions.newOutboundConnectionException(peer));
} }
// Initiate connection or return existing connection final CompletableFuture<PeerConnection> peerConnectionCompletableFuture;
final AtomicReference<CompletableFuture<PeerConnection>> connectionFuture = if (checkWhetherToConnect(peer, false)) {
new AtomicReference<>(); try {
connectionsById.compute( synchronized (this) {
peer.getId(), peerConnectionCompletableFuture =
(id, existingConnection) -> { peersConnectingCache.get(
if (existingConnection != null && !existingConnection.isFailedOrDisconnected()) { peer.getId(), () -> createPeerConnectionCompletableFuture(peer));
// We're already connected or connecting }
connectionFuture.set(existingConnection.getFuture()); } catch (final ExecutionException e) {
return existingConnection; throw new RuntimeException(e);
} else { }
// We're initiating a new connection } else {
final CompletableFuture<PeerConnection> future = initiateOutboundConnection(peer); final String errorMsg =
connectionFuture.set(future); "None of the ProtocolManagers wants to connect to peer " + peer.getId();
final RlpxConnection newConnection = RlpxConnection.outboundConnection(peer, future); LOG.trace(errorMsg);
newConnection.subscribeConnectionEstablished( return CompletableFuture.failedFuture((new RuntimeException(errorMsg)));
(conn) -> { }
this.dispatchConnect(conn.getPeerConnection());
this.enforceConnectionLimits(); return peerConnectionCompletableFuture;
}, }
(failedConn) -> cleanUpPeerConnection(failedConn.getId()));
return newConnection; @NotNull
private CompletableFuture<PeerConnection> createPeerConnectionCompletableFuture(final Peer peer) {
final CompletableFuture<PeerConnection> peerConnectionCompletableFuture =
initiateOutboundConnection(peer);
peerConnectionCompletableFuture.whenComplete(
(peerConnection, throwable) -> {
if (throwable == null) {
dispatchConnect(peerConnection);
} }
}); });
return peerConnectionCompletableFuture;
}
LOG.atTrace().setMessage("{}").addArgument(this::logConnectionsByIdToString).log(); private boolean checkWhetherToConnect(final Peer peer, final boolean incoming) {
return connectRequestSubscribers.stream()
return connectionFuture.get(); .anyMatch(callback -> callback.shouldConnect(peer, incoming));
} }
private void setupListeners() { private void setupListeners() {
connectionInitializer.subscribeIncomingConnect(this::handleIncomingConnection); connectionInitializer.subscribeIncomingConnect(this::handleIncomingConnection);
connectionEvents.subscribeDisconnect(this::handleDisconnect);
peerPermissions.subscribeUpdate(this::handlePermissionsUpdate); peerPermissions.subscribeUpdate(this::handlePermissionsUpdate);
} }
private void handleDisconnect(
final PeerConnection peerConnection,
final DisconnectReason disconnectReason,
final boolean initiatedByPeer) {
LOG.atTrace().setMessage("{}").addArgument(this::logConnectionsByIdToString).log();
cleanUpPeerConnection(peerConnection.getPeer().getId());
}
private void cleanUpPeerConnection(final Bytes peerId) {
connectionsById.compute(
peerId,
(id, trackedConnection) -> {
if (isNull(trackedConnection) || trackedConnection.isFailedOrDisconnected()) {
// Remove if failed or disconnected
return null;
}
return trackedConnection;
});
}
private void handlePermissionsUpdate( private void handlePermissionsUpdate(
final boolean permissionsRestricted, final Optional<List<Peer>> peers) { final boolean permissionsRestricted, final Optional<List<Peer>> peers) {
if (!permissionsRestricted) { if (!permissionsRestricted) {
@ -307,23 +270,23 @@ public class RlpxAgent {
return; return;
} }
final List<RlpxConnection> connectionsToCheck = final Stream<PeerConnection> connectionsToCheck;
peers if (peers.isPresent()) {
.map( final List<Bytes> changedPeersIds =
updatedPeers -> peers.get().stream().map(p -> p.getId()).collect(Collectors.toList());
updatedPeers.stream() connectionsToCheck =
.map(peer -> connectionsById.get(peer.getId())) streamConnections().filter(c -> changedPeersIds.contains(c.getPeer().getId()));
.filter(connection -> !isNull(connection)) } else {
.collect(Collectors.toList())) connectionsToCheck = streamConnections();
.orElse(new ArrayList<>(connectionsById.values())); }
connectionsToCheck.forEach( connectionsToCheck.forEach(
connection -> { connection -> {
if (!peerPermissions.allowOngoingConnection( if (!peerPermissions.allowOngoingConnection(
connection.getPeer(), connection.initiatedRemotely())) { connection.getPeer(), connection.inboundInitiated())) {
LOG.debug( LOG.debug(
"Disconnecting from peer that is not permitted to maintain ongoing connection: {}", "Disconnecting from peer that is not permitted to maintain ongoing connection: {}",
connection.getPeerConnection()); connection);
connection.disconnect(DisconnectReason.REQUESTED); connection.disconnect(DisconnectReason.REQUESTED);
} }
}); });
@ -347,6 +310,10 @@ public class RlpxAgent {
}); });
} }
public boolean canExceedConnectionLimits(final Peer peer) {
return peerPrivileges.canExceedConnectionLimits(peer);
}
private void handleIncomingConnection(final PeerConnection peerConnection) { private void handleIncomingConnection(final PeerConnection peerConnection) {
final Peer peer = peerConnection.getPeer(); final Peer peer = peerConnection.getPeer();
// Deny connection if our local node isn't ready // Deny connection if our local node isn't ready
@ -355,28 +322,7 @@ public class RlpxAgent {
peerConnection.disconnect(DisconnectReason.UNKNOWN); peerConnection.disconnect(DisconnectReason.UNKNOWN);
return; return;
} }
if (!randomPeerPriority) {
// Disconnect if too many peers
if (!peerPrivileges.canExceedConnectionLimits(peer)
&& getConnectionCount() >= upperBoundConnections) {
LOG.debug(
"Too many peers. Disconnect incoming connection: {} currentCount {}, max {}",
peerConnection,
getConnectionCount(),
upperBoundConnections);
peerConnection.disconnect(DisconnectReason.TOO_MANY_PEERS);
return;
}
// Disconnect if too many remotely-initiated connections
if (!peerPrivileges.canExceedConnectionLimits(peer) && remoteConnectionLimitReached()) {
LOG.debug(
"Too many remotely-initiated connections. Disconnect incoming connection: {}, maxRemote={}",
peerConnection,
maxRemotelyInitiatedConnections);
peerConnection.disconnect(DisconnectReason.TOO_MANY_PEERS);
return;
}
}
// Disconnect if not permitted // Disconnect if not permitted
if (!peerPermissions.allowNewInboundConnectionFrom(peer)) { if (!peerPermissions.allowNewInboundConnectionFrom(peer)) {
LOG.debug( LOG.debug(
@ -385,171 +331,11 @@ public class RlpxAgent {
return; return;
} }
// Track this new connection, deduplicating existing connection if necessary if (checkWhetherToConnect(peer, true)) {
final AtomicBoolean newConnectionAccepted = new AtomicBoolean(false);
final RlpxConnection inboundConnection = RlpxConnection.inboundConnection(peerConnection);
// Our disconnect handler runs connectionsById.compute(), so don't actually execute the
// disconnect command until we've returned from our compute() calculation
final AtomicReference<Runnable> disconnectAction = new AtomicReference<>();
connectionsById.compute(
peer.getId(),
(nodeId, existingConnection) -> {
if (existingConnection == null) {
// The new connection is unique, set it and return
LOG.debug("Inbound connection established with {}", peerConnection.getPeer().getId());
newConnectionAccepted.set(true);
return inboundConnection;
}
// We already have an existing connection, figure out which connection to keep
if (compareDuplicateConnections(inboundConnection, existingConnection) < 0) {
// Keep the inbound connection
LOG.debug(
"Duplicate connection detected, disconnecting previously established connection in favor of new inbound connection for peer: {}",
peerConnection.getPeer().getId());
disconnectAction.set(
() -> existingConnection.disconnect(DisconnectReason.ALREADY_CONNECTED));
newConnectionAccepted.set(true);
return inboundConnection;
} else {
// Keep the existing connection
LOG.debug(
"Duplicate connection detected, disconnecting inbound connection in favor of previously established connection for peer: {}",
peerConnection.getPeer().getId());
disconnectAction.set(
() -> inboundConnection.disconnect(DisconnectReason.ALREADY_CONNECTED));
return existingConnection;
}
});
if (!isNull(disconnectAction.get())) {
disconnectAction.get().run();
}
if (newConnectionAccepted.get()) {
dispatchConnect(peerConnection); dispatchConnect(peerConnection);
}
// Check remote connections again to control for race conditions
enforceRemoteConnectionLimits();
enforceConnectionLimits();
LOG.atTrace().setMessage("{}").addArgument(this::logConnectionsByIdToString).log();
}
private boolean shouldLimitRemoteConnections() {
return maxRemotelyInitiatedConnections < upperBoundConnections;
}
private boolean remoteConnectionLimitReached() {
return shouldLimitRemoteConnections()
&& countUntrustedRemotelyInitiatedConnections() >= maxRemotelyInitiatedConnections;
}
private long countUntrustedRemotelyInitiatedConnections() {
return connectionsById.values().stream()
.filter(RlpxConnection::isActive)
.filter(RlpxConnection::initiatedRemotely)
.filter(conn -> !peerPrivileges.canExceedConnectionLimits(conn.getPeer()))
.count();
}
private void enforceRemoteConnectionLimits() {
if (!shouldLimitRemoteConnections()
|| connectionsById.size() < maxRemotelyInitiatedConnections) {
// Nothing to do
return;
}
getActivePrioritizedConnections()
.filter(RlpxConnection::initiatedRemotely)
.filter(conn -> !peerPrivileges.canExceedConnectionLimits(conn.getPeer()))
.skip(maxRemotelyInitiatedConnections)
.forEach(
conn -> {
LOG.debug(
"Too many remotely initiated connections. Disconnect low-priority connection: {}, maxRemote={}",
conn,
maxRemotelyInitiatedConnections);
conn.disconnect(DisconnectReason.TOO_MANY_PEERS);
});
}
private void enforceConnectionLimits() {
if (connectionsById.size() < upperBoundConnections) {
// Nothing to do - we're under our limits
return;
}
getActivePrioritizedConnections()
.skip(upperBoundConnections)
.filter(c -> !peerPrivileges.canExceedConnectionLimits(c.getPeer()))
.forEach(
conn -> {
LOG.debug(
"Too many connections. Disconnect low-priority connection: {}, maxConnections={}",
conn,
upperBoundConnections);
conn.disconnect(DisconnectReason.TOO_MANY_PEERS);
});
}
private Stream<RlpxConnection> getActivePrioritizedConnections() {
return connectionsById.values().stream()
.filter(RlpxConnection::isActive)
.sorted(this::comparePeerPriorities);
}
private int comparePeerPriorities(final RlpxConnection a, final RlpxConnection b) {
final boolean aIgnoresPeerLimits = peerPrivileges.canExceedConnectionLimits(a.getPeer());
final boolean bIgnoresPeerLimits = peerPrivileges.canExceedConnectionLimits(b.getPeer());
if (aIgnoresPeerLimits && !bIgnoresPeerLimits) {
return -1;
} else if (bIgnoresPeerLimits && !aIgnoresPeerLimits) {
return 1;
} else { } else {
return randomPeerPriority peerConnection.disconnect(DisconnectReason.UNKNOWN);
? compareByMaskedNodeId(a, b)
: compareConnectionInitiationTimes(a, b);
}
}
private int compareConnectionInitiationTimes(final RlpxConnection a, final RlpxConnection b) {
return Math.toIntExact(a.getInitiatedAt() - b.getInitiatedAt());
}
private int compareByMaskedNodeId(final RlpxConnection a, final RlpxConnection b) {
return a.getPeer().getId().xor(nodeIdMask).compareTo(b.getPeer().getId().xor(nodeIdMask));
}
/**
* Compares two connections to the same peer to determine which connection should be kept
*
* @param a The first connection
* @param b The second connection
* @return A negative value if {@code a} should be kept, a positive value is {@code b} should be
* kept
*/
private int compareDuplicateConnections(final RlpxConnection a, final RlpxConnection b) {
checkState(localNode.isReady());
checkState(Objects.equals(a.getPeer().getId(), b.getPeer().getId()));
if (a.isFailedOrDisconnected() != b.isFailedOrDisconnected()) {
// One connection has failed - prioritize the one that hasn't failed
return a.isFailedOrDisconnected() ? 1 : -1;
}
final Bytes peerId = a.getPeer().getId();
final Bytes localId = localNode.getPeer().getId();
// at this point a.Id == b.Id
if (a.initiatedRemotely() != b.initiatedRemotely()) {
// If we have connections initiated in different directions, keep the connection initiated
// by the node with the lower id
if (localId.compareTo(peerId) < 0) {
return a.initiatedLocally() ? -1 : 1;
} else {
return a.initiatedLocally() ? 1 : -1;
}
} }
// Otherwise, keep older connection
LOG.debug("comparing timestamps " + a.getInitiatedAt() + " with " + b.getInitiatedAt());
return Math.toIntExact(a.getInitiatedAt() - b.getInitiatedAt());
} }
public void subscribeMessage(final Capability capability, final MessageCallback callback) { public void subscribeMessage(final Capability capability, final MessageCallback callback) {
@ -560,15 +346,27 @@ public class RlpxAgent {
connectSubscribers.subscribe(callback); connectSubscribers.subscribe(callback);
} }
public void subscribeConnectRequest(final ShouldConnectCallback callback) {
connectRequestSubscribers.add(callback);
}
public void subscribeDisconnect(final DisconnectCallback callback) { public void subscribeDisconnect(final DisconnectCallback callback) {
connectionEvents.subscribeDisconnect(callback); connectionEvents.subscribeDisconnect(callback);
} }
private void dispatchConnect(final PeerConnection connection) { private void dispatchConnect(final PeerConnection connection) {
connectedPeersCounter.inc();
connectSubscribers.forEach(c -> c.onConnect(connection)); connectSubscribers.forEach(c -> c.onConnect(connection));
} }
@VisibleForTesting
public ConcurrentMap<Bytes, CompletableFuture<PeerConnection>> getMapOfCompletableFutures() {
return peersConnectingCache.asMap();
}
public int getPeerLowerBound() {
return lowerBound;
}
public static class Builder { public static class Builder {
private NodeKey nodeKey; private NodeKey nodeKey;
private LocalNode localNode; private LocalNode localNode;
@ -577,9 +375,11 @@ public class RlpxAgent {
private PeerPermissions peerPermissions; private PeerPermissions peerPermissions;
private ConnectionInitializer connectionInitializer; private ConnectionInitializer connectionInitializer;
private PeerConnectionEvents connectionEvents; private PeerConnectionEvents connectionEvents;
private boolean randomPeerPriority;
private MetricsSystem metricsSystem; private MetricsSystem metricsSystem;
private Optional<TLSConfiguration> p2pTLSConfiguration; private Optional<TLSConfiguration> p2pTLSConfiguration;
private Supplier<Stream<PeerConnection>> allConnectionsSupplier;
private Supplier<Stream<PeerConnection>> allActiveConnectionsSupplier;
private int peersLowerBound;
private Builder() {} private Builder() {}
@ -616,11 +416,9 @@ public class RlpxAgent {
connectionInitializer, connectionInitializer,
rlpxPermissions, rlpxPermissions,
peerPrivileges, peerPrivileges,
config.getPeerUpperBound(), peersLowerBound,
config.getPeerLowerBound(), allConnectionsSupplier,
config.getMaxRemotelyInitiatedConnections(), allActiveConnectionsSupplier);
randomPeerPriority,
metricsSystem);
} }
private void validate() { private void validate() {
@ -680,13 +478,25 @@ public class RlpxAgent {
return this; return this;
} }
public Builder randomPeerPriority(final boolean randomPeerPriority) { public Builder p2pTLSConfiguration(final Optional<TLSConfiguration> p2pTLSConfiguration) {
this.randomPeerPriority = randomPeerPriority; this.p2pTLSConfiguration = p2pTLSConfiguration;
return this; return this;
} }
public Builder p2pTLSConfiguration(final Optional<TLSConfiguration> p2pTLSConfiguration) { public Builder allConnectionsSupplier(
this.p2pTLSConfiguration = p2pTLSConfiguration; final Supplier<Stream<PeerConnection>> allConnectionsSupplier) {
this.allConnectionsSupplier = allConnectionsSupplier;
return this;
}
public Builder allActiveConnectionsSupplier(
final Supplier<Stream<PeerConnection>> allActiveConnectionsSupplier) {
this.allActiveConnectionsSupplier = allActiveConnectionsSupplier;
return this;
}
public Builder peersLowerBound(final int peersLowerBound) {
this.peersLowerBound = peersLowerBound;
return this; return this;
} }
} }

@ -32,9 +32,7 @@ import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Set; import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;
import com.google.common.base.MoreObjects;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -53,6 +51,10 @@ public abstract class AbstractPeerConnection implements PeerConnection {
private final AtomicBoolean disconnected = new AtomicBoolean(false); private final AtomicBoolean disconnected = new AtomicBoolean(false);
protected final PeerConnectionEventDispatcher connectionEventDispatcher; protected final PeerConnectionEventDispatcher connectionEventDispatcher;
private final LabelledMetric<Counter> outboundMessagesCounter; private final LabelledMetric<Counter> outboundMessagesCounter;
private final long initiatedAt;
private final boolean inboundInitiated;
private boolean statusSent;
private boolean statusReceived;
protected AbstractPeerConnection( protected AbstractPeerConnection(
final Peer peer, final Peer peer,
@ -62,7 +64,8 @@ public abstract class AbstractPeerConnection implements PeerConnection {
final String connectionId, final String connectionId,
final CapabilityMultiplexer multiplexer, final CapabilityMultiplexer multiplexer,
final PeerConnectionEventDispatcher connectionEventDispatcher, final PeerConnectionEventDispatcher connectionEventDispatcher,
final LabelledMetric<Counter> outboundMessagesCounter) { final LabelledMetric<Counter> outboundMessagesCounter,
final boolean inboundInitiated) {
this.peer = peer; this.peer = peer;
this.peerInfo = peerInfo; this.peerInfo = peerInfo;
this.localAddress = localAddress; this.localAddress = localAddress;
@ -76,6 +79,10 @@ public abstract class AbstractPeerConnection implements PeerConnection {
} }
this.connectionEventDispatcher = connectionEventDispatcher; this.connectionEventDispatcher = connectionEventDispatcher;
this.outboundMessagesCounter = outboundMessagesCounter; this.outboundMessagesCounter = outboundMessagesCounter;
this.inboundInitiated = inboundInitiated;
this.initiatedAt = System.currentTimeMillis();
LOG.debug("New PeerConnection ({}) established with peer {}", this, peer.getId());
} }
@Override @Override
@ -147,7 +154,7 @@ public abstract class AbstractPeerConnection implements PeerConnection {
// Always ensure the context gets closed immediately even if we previously sent a disconnect // Always ensure the context gets closed immediately even if we previously sent a disconnect
// message and are waiting to close. // message and are waiting to close.
closeConnectionImmediately(); closeConnectionImmediately();
LOG.debug("Terminating connection {}, reason {}", System.identityHashCode(this), reason); LOG.debug("Terminating connection {}, reason {}", this, reason);
} }
protected abstract void closeConnectionImmediately(); protected abstract void closeConnectionImmediately();
@ -179,6 +186,16 @@ public abstract class AbstractPeerConnection implements PeerConnection {
return remoteAddress; return remoteAddress;
} }
@Override
public long getInitiatedAt() {
return initiatedAt;
}
@Override
public boolean inboundInitiated() {
return inboundInitiated;
}
@Override @Override
public boolean equals(final Object o) { public boolean equals(final Object o) {
if (o == this) { if (o == this) {
@ -197,14 +214,31 @@ public abstract class AbstractPeerConnection implements PeerConnection {
return Objects.hash(connectionId, peer); return Objects.hash(connectionId, peer);
} }
@Override
public void setStatusSent() {
this.statusSent = true;
}
@Override
public void setStatusReceived() {
this.statusReceived = true;
}
@Override
public boolean getStatusExchanged() {
return statusReceived && statusSent;
}
@Override @Override
public String toString() { public String toString() {
return MoreObjects.toStringHelper(this) return "[Connection with hashCode "
.add("nodeId", peerInfo.getNodeId()) + hashCode()
.add("clientId", peerInfo.getClientId()) + " with peer "
.add( + this.peer.getId()
"caps", + " inboundInitiated "
agreedCapabilities.stream().map(Capability::toString).collect(Collectors.joining(", "))) + inboundInitiated
.toString(); + " initAt "
+ initiatedAt
+ "]";
} }
} }

@ -122,4 +122,20 @@ public interface PeerConnection {
default EnodeURL getRemoteEnode() { default EnodeURL getRemoteEnode() {
return getPeer().getEnodeURL(); return getPeer().getEnodeURL();
} }
/**
* Get the difference, measured in milliseconds, between the time this connection was initiated
* and midnight, January 1, 1970 UTC
*
* @return the time when this connection was initiated.
*/
long getInitiatedAt();
boolean inboundInitiated();
void setStatusSent();
void setStatusReceived();
boolean getStatusExchanged();
} }

@ -1,254 +0,0 @@
/*
* Copyright ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.p2p.rlpx.connections;
import org.hyperledger.besu.ethereum.p2p.peers.Peer;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.messages.DisconnectMessage.DisconnectReason;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import org.apache.tuweni.bytes.Bytes;
public abstract class RlpxConnection {
private final long initiatedAt;
protected final CompletableFuture<PeerConnection> future;
private RlpxConnection(final CompletableFuture<PeerConnection> future) {
this.future = future;
this.initiatedAt = System.currentTimeMillis();
}
public static RlpxConnection inboundConnection(final PeerConnection peerConnection) {
return new RemotelyInitiatedRlpxConnection(peerConnection);
}
public static RlpxConnection outboundConnection(
final Peer peer, final CompletableFuture<PeerConnection> future) {
return new LocallyInitiatedRlpxConnection(peer, future);
}
public abstract Peer getPeer();
public abstract void disconnect(DisconnectReason reason);
public Bytes getId() {
return getPeer().getId();
}
public abstract PeerConnection getPeerConnection() throws ConnectionNotEstablishedException;
public CompletableFuture<PeerConnection> getFuture() {
return future;
}
public abstract boolean isActive();
public abstract boolean isPending();
public abstract boolean isFailedOrDisconnected();
public abstract boolean initiatedRemotely();
public void subscribeConnectionEstablished(
final RlpxConnectCallback successCallback, final RlpxConnectFailedCallback failedCallback) {
future.whenComplete(
(conn, err) -> {
if (err != null) {
failedCallback.onFailure(this);
} else {
successCallback.onConnect(this);
}
});
}
public boolean initiatedLocally() {
return !initiatedRemotely();
}
public long getInitiatedAt() {
return initiatedAt;
}
private static class RemotelyInitiatedRlpxConnection extends RlpxConnection {
private final PeerConnection peerConnection;
private RemotelyInitiatedRlpxConnection(final PeerConnection peerConnection) {
super(CompletableFuture.completedFuture(peerConnection));
this.peerConnection = peerConnection;
}
@Override
public Peer getPeer() {
return peerConnection.getPeer();
}
@Override
public void disconnect(final DisconnectReason reason) {
peerConnection.disconnect(reason);
}
@Override
public PeerConnection getPeerConnection() {
return peerConnection;
}
@Override
public boolean isActive() {
return !peerConnection.isDisconnected();
}
@Override
public boolean isPending() {
return false;
}
@Override
public boolean isFailedOrDisconnected() {
return peerConnection.isDisconnected();
}
@Override
public boolean initiatedRemotely() {
return true;
}
@Override
public boolean equals(final Object o) {
if (o == this) {
return true;
}
if (!(o instanceof RemotelyInitiatedRlpxConnection)) {
return false;
}
final RemotelyInitiatedRlpxConnection that = (RemotelyInitiatedRlpxConnection) o;
return Objects.equals(peerConnection, that.peerConnection);
}
@Override
public int hashCode() {
return Objects.hash(peerConnection);
}
@Override
public String toString() {
return "RemotelyInitiatedRlpxConnection initiatedAt:"
+ getInitiatedAt()
+ " to "
+ peerConnection.getPeer().getId()
+ " disconnected? "
+ isFailedOrDisconnected();
}
}
private static class LocallyInitiatedRlpxConnection extends RlpxConnection {
private final Peer peer;
private LocallyInitiatedRlpxConnection(
final Peer peer, final CompletableFuture<PeerConnection> future) {
super(future);
this.peer = peer;
}
@Override
public Peer getPeer() {
return peer;
}
@Override
public void disconnect(final DisconnectReason reason) {
future.thenAccept((conn) -> conn.disconnect(reason));
}
@Override
public PeerConnection getPeerConnection() throws ConnectionNotEstablishedException {
if (!future.isDone() || future.isCompletedExceptionally()) {
throw new ConnectionNotEstablishedException(
"Cannot access PeerConnection before connection is fully established.");
}
return future.getNow(null);
}
@Override
public boolean isActive() {
return future.isDone()
&& !future.isCompletedExceptionally()
&& !getPeerConnection().isDisconnected();
}
@Override
public boolean isPending() {
return !future.isDone();
}
@Override
public boolean isFailedOrDisconnected() {
return future.isCompletedExceptionally()
|| (future.isDone() && getPeerConnection().isDisconnected());
}
@Override
public boolean initiatedRemotely() {
return false;
}
@Override
public boolean equals(final Object o) {
if (o == this) {
return true;
}
if (!(o instanceof LocallyInitiatedRlpxConnection)) {
return false;
}
final LocallyInitiatedRlpxConnection that = (LocallyInitiatedRlpxConnection) o;
return Objects.equals(peer, that.peer) && Objects.equals(future, that.future);
}
@Override
public int hashCode() {
return Objects.hash(peer, future);
}
@Override
public String toString() {
return "LocallyInitiatedRlpxConnection initiatedAt:"
+ getInitiatedAt()
+ " to "
+ getPeer().getId()
+ " disconnected? "
+ isFailedOrDisconnected();
}
}
public static class ConnectionNotEstablishedException extends IllegalStateException {
public ConnectionNotEstablishedException(final String message) {
super(message);
}
}
@FunctionalInterface
public interface RlpxConnectCallback {
void onConnect(RlpxConnection connection);
}
@FunctionalInterface
public interface RlpxConnectFailedCallback {
void onFailure(RlpxConnection connection);
}
}

@ -59,6 +59,7 @@ abstract class AbstractHandshakeHandler extends SimpleChannelInboundHandler<Byte
private final MetricsSystem metricsSystem; private final MetricsSystem metricsSystem;
private final FramerProvider framerProvider; private final FramerProvider framerProvider;
private final boolean inboundInitiated;
AbstractHandshakeHandler( AbstractHandshakeHandler(
final List<SubProtocol> subProtocols, final List<SubProtocol> subProtocols,
@ -68,7 +69,8 @@ abstract class AbstractHandshakeHandler extends SimpleChannelInboundHandler<Byte
final PeerConnectionEventDispatcher connectionEventDispatcher, final PeerConnectionEventDispatcher connectionEventDispatcher,
final MetricsSystem metricsSystem, final MetricsSystem metricsSystem,
final HandshakerProvider handshakerProvider, final HandshakerProvider handshakerProvider,
final FramerProvider framerProvider) { final FramerProvider framerProvider,
final boolean inboundInitiated) {
this.subProtocols = subProtocols; this.subProtocols = subProtocols;
this.localNode = localNode; this.localNode = localNode;
this.expectedPeer = expectedPeer; this.expectedPeer = expectedPeer;
@ -77,6 +79,7 @@ abstract class AbstractHandshakeHandler extends SimpleChannelInboundHandler<Byte
this.metricsSystem = metricsSystem; this.metricsSystem = metricsSystem;
this.handshaker = handshakerProvider.buildInstance(); this.handshaker = handshakerProvider.buildInstance();
this.framerProvider = framerProvider; this.framerProvider = framerProvider;
this.inboundInitiated = inboundInitiated;
} }
/** /**
@ -117,7 +120,8 @@ abstract class AbstractHandshakeHandler extends SimpleChannelInboundHandler<Byte
expectedPeer, expectedPeer,
connectionEventDispatcher, connectionEventDispatcher,
connectionFuture, connectionFuture,
metricsSystem); metricsSystem,
inboundInitiated);
ctx.channel() ctx.channel()
.pipeline() .pipeline()

@ -69,6 +69,7 @@ final class DeFramer extends ByteToMessageDecoder {
// The peer we are expecting to connect to, if such a peer is known // The peer we are expecting to connect to, if such a peer is known
private final Optional<Peer> expectedPeer; private final Optional<Peer> expectedPeer;
private final List<SubProtocol> subProtocols; private final List<SubProtocol> subProtocols;
private final boolean inboundInitiated;
private boolean hellosExchanged; private boolean hellosExchanged;
private final LabelledMetric<Counter> outboundMessagesCounter; private final LabelledMetric<Counter> outboundMessagesCounter;
@ -79,13 +80,15 @@ final class DeFramer extends ByteToMessageDecoder {
final Optional<Peer> expectedPeer, final Optional<Peer> expectedPeer,
final PeerConnectionEventDispatcher connectionEventDispatcher, final PeerConnectionEventDispatcher connectionEventDispatcher,
final CompletableFuture<PeerConnection> connectFuture, final CompletableFuture<PeerConnection> connectFuture,
final MetricsSystem metricsSystem) { final MetricsSystem metricsSystem,
final boolean inboundInitiated) {
this.framer = framer; this.framer = framer;
this.subProtocols = subProtocols; this.subProtocols = subProtocols;
this.localNode = localNode; this.localNode = localNode;
this.expectedPeer = expectedPeer; this.expectedPeer = expectedPeer;
this.connectFuture = connectFuture; this.connectFuture = connectFuture;
this.connectionEventDispatcher = connectionEventDispatcher; this.connectionEventDispatcher = connectionEventDispatcher;
this.inboundInitiated = inboundInitiated;
this.outboundMessagesCounter = this.outboundMessagesCounter =
metricsSystem.createLabelledCounter( metricsSystem.createLabelledCounter(
BesuMetricCategory.NETWORK, BesuMetricCategory.NETWORK,
@ -140,7 +143,8 @@ final class DeFramer extends ByteToMessageDecoder {
peerInfo, peerInfo,
capabilityMultiplexer, capabilityMultiplexer,
connectionEventDispatcher, connectionEventDispatcher,
outboundMessagesCounter); outboundMessagesCounter,
inboundInitiated);
// Check peer is who we expected // Check peer is who we expected
if (expectedPeer.isPresent() if (expectedPeer.isPresent()

@ -49,7 +49,8 @@ final class HandshakeHandlerInbound extends AbstractHandshakeHandler {
connectionEventDispatcher, connectionEventDispatcher,
metricsSystem, metricsSystem,
handshakerProvider, handshakerProvider,
framerProvider); framerProvider,
true);
handshaker.prepareResponder(nodeKey); handshaker.prepareResponder(nodeKey);
} }

@ -59,7 +59,8 @@ final class HandshakeHandlerOutbound extends AbstractHandshakeHandler {
connectionEventDispatcher, connectionEventDispatcher,
metricsSystem, metricsSystem,
handshakerProvider, handshakerProvider,
framerProvider); framerProvider,
false);
handshaker.prepareInitiator( handshaker.prepareInitiator(
nodeKey, SignatureAlgorithmFactory.getInstance().createPublicKey(peer.getId())); nodeKey, SignatureAlgorithmFactory.getInstance().createPublicKey(peer.getId()));
this.first = handshaker.firstMessage(); this.first = handshaker.firstMessage();

@ -138,7 +138,7 @@ public class NettyConnectionInitializer
@Override @Override
public CompletableFuture<Void> stop() { public CompletableFuture<Void> stop() {
CompletableFuture<Void> stoppedFuture = new CompletableFuture<>(); final CompletableFuture<Void> stoppedFuture = new CompletableFuture<>();
if (!started.get() || !stopped.compareAndSet(false, true)) { if (!started.get() || !stopped.compareAndSet(false, true)) {
stoppedFuture.completeExceptionally( stoppedFuture.completeExceptionally(
new IllegalStateException("Illegal attempt to stop " + this.getClass().getSimpleName())); new IllegalStateException("Illegal attempt to stop " + this.getClass().getSimpleName()));

@ -43,7 +43,8 @@ final class NettyPeerConnection extends AbstractPeerConnection {
final PeerInfo peerInfo, final PeerInfo peerInfo,
final CapabilityMultiplexer multiplexer, final CapabilityMultiplexer multiplexer,
final PeerConnectionEventDispatcher connectionEventDispatcher, final PeerConnectionEventDispatcher connectionEventDispatcher,
final LabelledMetric<Counter> outboundMessagesCounter) { final LabelledMetric<Counter> outboundMessagesCounter,
final boolean inboundInitiated) {
super( super(
peer, peer,
peerInfo, peerInfo,
@ -52,7 +53,8 @@ final class NettyPeerConnection extends AbstractPeerConnection {
ctx.channel().id().asLongText(), ctx.channel().id().asLongText(),
multiplexer, multiplexer,
connectionEventDispatcher, connectionEventDispatcher,
outboundMessagesCounter); outboundMessagesCounter,
inboundInitiated);
this.ctx = ctx; this.ctx = ctx;
ctx.channel() ctx.channel()

@ -0,0 +1,23 @@
/*
* 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.rlpx.wire;
import org.hyperledger.besu.ethereum.p2p.peers.Peer;
@FunctionalInterface
public interface ShouldConnectCallback {
boolean shouldConnect(final Peer peer, final boolean incoming);
}

@ -1,66 +0,0 @@
/*
* Copyright ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.p2p.config;
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.Test;
public class RlpxConfigurationTest {
@Test
public void getMaxRemotelyInitiatedConnections_remoteLimitsDisabled() {
final RlpxConfiguration config =
RlpxConfiguration.create()
.setFractionRemoteWireConnectionsAllowed(.5f)
.setLimitRemoteWireConnectionsEnabled(false)
.setPeerUpperBound(20);
assertThat(config.getMaxRemotelyInitiatedConnections()).isEqualTo(20);
}
@Test
public void getMaxRemotelyInitiatedConnections_remoteLimitsEnabled() {
final RlpxConfiguration config =
RlpxConfiguration.create()
.setFractionRemoteWireConnectionsAllowed(.5f)
.setLimitRemoteWireConnectionsEnabled(true)
.setPeerUpperBound(20);
assertThat(config.getMaxRemotelyInitiatedConnections()).isEqualTo(10);
}
@Test
public void getMaxRemotelyInitiatedConnections_remoteLimitsEnabledWithNonIntegerRatio() {
final RlpxConfiguration config =
RlpxConfiguration.create()
.setFractionRemoteWireConnectionsAllowed(.5f)
.setLimitRemoteWireConnectionsEnabled(true)
.setPeerUpperBound(25);
assertThat(config.getMaxRemotelyInitiatedConnections()).isEqualTo(12);
}
@Test
public void getMaxRemotelyInitiatedConnections_remoteLimitsEnabledRoundsToZero() {
final RlpxConfiguration config =
RlpxConfiguration.create()
.setFractionRemoteWireConnectionsAllowed(.5f)
.setLimitRemoteWireConnectionsEnabled(true)
.setPeerUpperBound(1);
assertThat(config.getMaxRemotelyInitiatedConnections()).isEqualTo(0);
}
}

@ -40,7 +40,6 @@ import org.hyperledger.besu.ethereum.p2p.peers.Peer;
import org.hyperledger.besu.ethereum.p2p.peers.PeerTestHelper; import org.hyperledger.besu.ethereum.p2p.peers.PeerTestHelper;
import org.hyperledger.besu.ethereum.p2p.rlpx.RlpxAgent; import org.hyperledger.besu.ethereum.p2p.rlpx.RlpxAgent;
import org.hyperledger.besu.ethereum.p2p.rlpx.connections.MockPeerConnection; import org.hyperledger.besu.ethereum.p2p.rlpx.connections.MockPeerConnection;
import org.hyperledger.besu.ethereum.p2p.rlpx.connections.PeerConnection;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.Capability; import org.hyperledger.besu.ethereum.p2p.rlpx.wire.Capability;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.MockSubProtocol; import org.hyperledger.besu.ethereum.p2p.rlpx.wire.MockSubProtocol;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.messages.DisconnectMessage.DisconnectReason; import org.hyperledger.besu.ethereum.p2p.rlpx.wire.messages.DisconnectMessage.DisconnectReason;
@ -196,25 +195,8 @@ public final class DefaultP2PNetworkTest {
maintainedPeers.add(peer); maintainedPeers.add(peer);
// Don't connect to an already connected peer // Don't connect to an already connected peer
final CompletableFuture<PeerConnection> connectionFuture = when(rlpxAgent.streamActiveConnections())
CompletableFuture.completedFuture(MockPeerConnection.create(peer)); .thenReturn(Stream.of(MockPeerConnection.create(peer)));
when(rlpxAgent.getPeerConnection(peer)).thenReturn(Optional.of(connectionFuture));
network.checkMaintainedConnectionPeers();
verify(rlpxAgent, times(0)).connect(peer);
}
@Test
public void checkMaintainedConnectionPeers_connectingPeer() {
final DefaultP2PNetwork network = network();
final Peer peer = PeerTestHelper.createPeer();
network.start();
maintainedPeers.add(peer);
// Don't connect when connection is already pending.
final CompletableFuture<PeerConnection> connectionFuture = new CompletableFuture<>();
when(rlpxAgent.getPeerConnection(peer)).thenReturn(Optional.of(connectionFuture));
network.checkMaintainedConnectionPeers(); network.checkMaintainedConnectionPeers();
verify(rlpxAgent, times(0)).connect(peer); verify(rlpxAgent, times(0)).connect(peer);
} }
@ -410,6 +392,8 @@ public final class DefaultP2PNetworkTest {
.supportedCapabilities(Capability.create("eth", 63)) .supportedCapabilities(Capability.create("eth", 63))
.storageProvider(new InMemoryKeyValueStorageProvider()) .storageProvider(new InMemoryKeyValueStorageProvider())
.blockNumberForks(Collections.emptyList()) .blockNumberForks(Collections.emptyList())
.timestampForks(Collections.emptyList()); .timestampForks(Collections.emptyList())
.allConnectionsSupplier(Stream::empty)
.allActiveConnectionsSupplier(Stream::empty);
} }
} }

@ -39,6 +39,7 @@ import org.hyperledger.besu.plugin.data.EnodeURL;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.stream.Stream;
import io.vertx.core.Vertx; import io.vertx.core.Vertx;
import org.assertj.core.api.Assertions; import org.assertj.core.api.Assertions;
@ -81,9 +82,12 @@ public class NetworkingServiceLifecycleTest {
final Block blockMock = mock(Block.class); final Block blockMock = mock(Block.class);
when(blockMock.getHash()).thenReturn(Hash.ZERO); when(blockMock.getHash()).thenReturn(Hash.ZERO);
when(blockchainMock.getGenesisBlock()).thenReturn(blockMock); when(blockchainMock.getGenesisBlock()).thenReturn(blockMock);
builder.blockchain(blockchainMock); builder
builder.blockNumberForks(Collections.emptyList()); .blockchain(blockchainMock)
builder.timestampForks(Collections.emptyList()); .blockNumberForks(Collections.emptyList())
.timestampForks(Collections.emptyList())
.allConnectionsSupplier(Stream::empty)
.allActiveConnectionsSupplier(Stream::empty);
return builder; return builder;
} }

@ -49,6 +49,7 @@ import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;
import io.vertx.core.Vertx; import io.vertx.core.Vertx;
import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes;
@ -79,6 +80,8 @@ public class P2PNetworkTest {
final NodeKey nodeKey = NodeKeyUtils.generate(); final NodeKey nodeKey = NodeKeyUtils.generate();
try (final P2PNetwork listener = builder().nodeKey(nodeKey).build(); try (final P2PNetwork listener = builder().nodeKey(nodeKey).build();
final P2PNetwork connector = builder().build()) { final P2PNetwork connector = builder().build()) {
listener.getRlpxAgent().subscribeConnectRequest((p, d) -> true);
connector.getRlpxAgent().subscribeConnectRequest((p, d) -> true);
listener.start(); listener.start();
connector.start(); connector.start();
@ -101,6 +104,8 @@ public class P2PNetworkTest {
final NodeKey listenNodeKey = NodeKeyUtils.generate(); final NodeKey listenNodeKey = NodeKeyUtils.generate();
try (final P2PNetwork listener = builder().nodeKey(listenNodeKey).build(); try (final P2PNetwork listener = builder().nodeKey(listenNodeKey).build();
final P2PNetwork connector = builder().build()) { final P2PNetwork connector = builder().build()) {
listener.getRlpxAgent().subscribeConnectRequest((p, d) -> true);
connector.getRlpxAgent().subscribeConnectRequest((p, d) -> true);
listener.start(); listener.start();
connector.start(); connector.start();
@ -123,67 +128,6 @@ public class P2PNetworkTest {
} }
} }
/**
* Tests that max peers setting is honoured and inbound connections that would exceed the limit
* are correctly disconnected.
*
* @throws Exception On Failure
*/
@Test
public void limitMaxPeers() throws Exception {
final NodeKey nodeKey = NodeKeyUtils.generate();
final int maxPeers = 1;
final NetworkingConfiguration listenerConfig =
NetworkingConfiguration.create()
.setDiscovery(DiscoveryConfiguration.create().setActive(false))
.setRlpx(
RlpxConfiguration.create()
.setBindPort(0)
.setPeerUpperBound(maxPeers)
.setSupportedProtocols(MockSubProtocol.create()));
try (final P2PNetwork listener = builder().nodeKey(nodeKey).config(listenerConfig).build();
final P2PNetwork connector1 = builder().build();
final P2PNetwork connector2 = builder().build()) {
// Setup listener and first connection
listener.start();
connector1.start();
final EnodeURL listenerEnode = listener.getLocalEnode().get();
final Bytes listenId = listenerEnode.getNodeId();
final int listenPort = listenerEnode.getListeningPort().get();
final Peer listeningPeer = createPeer(listenId, listenPort);
Assertions.assertThat(
connector1
.connect(listeningPeer)
.get(30L, TimeUnit.SECONDS)
.getPeerInfo()
.getNodeId())
.isEqualTo(listenId);
// Setup second connection and check that connection is not accepted
final CompletableFuture<PeerConnection> peerFuture = new CompletableFuture<>();
final CompletableFuture<DisconnectReason> reasonFuture = new CompletableFuture<>();
connector2.subscribeDisconnect(
(peerConnection, reason, initiatedByPeer) -> {
peerFuture.complete(peerConnection);
reasonFuture.complete(reason);
});
connector2.start();
Assertions.assertThat(
connector2
.connect(listeningPeer)
.get(30L, TimeUnit.SECONDS)
.getPeerInfo()
.getNodeId())
.isEqualTo(listenId);
Assertions.assertThat(peerFuture.get(30L, TimeUnit.SECONDS).getPeerInfo().getNodeId())
.isEqualTo(listenId);
assertThat(reasonFuture.get(30L, TimeUnit.SECONDS))
.isEqualByComparingTo(DisconnectReason.TOO_MANY_PEERS);
}
}
@Test @Test
public void rejectPeerWithNoSharedCaps() throws Exception { public void rejectPeerWithNoSharedCaps() throws Exception {
final NodeKey listenerNodeKey = NodeKeyUtils.generate(); final NodeKey listenerNodeKey = NodeKeyUtils.generate();
@ -197,6 +141,9 @@ public class P2PNetworkTest {
builder().nodeKey(listenerNodeKey).supportedCapabilities(cap1).build(); builder().nodeKey(listenerNodeKey).supportedCapabilities(cap1).build();
final P2PNetwork connector = final P2PNetwork connector =
builder().nodeKey(connectorNodeKey).supportedCapabilities(cap2).build()) { builder().nodeKey(connectorNodeKey).supportedCapabilities(cap2).build()) {
listener.getRlpxAgent().subscribeConnectRequest((p, d) -> true);
connector.getRlpxAgent().subscribeConnectRequest((p, d) -> true);
listener.start(); listener.start();
connector.start(); connector.start();
final EnodeURL listenerEnode = listener.getLocalEnode().get(); final EnodeURL listenerEnode = listener.getLocalEnode().get();
@ -215,6 +162,8 @@ public class P2PNetworkTest {
try (final P2PNetwork localNetwork = builder().peerPermissions(localDenylist).build(); try (final P2PNetwork localNetwork = builder().peerPermissions(localDenylist).build();
final P2PNetwork remoteNetwork = builder().build()) { final P2PNetwork remoteNetwork = builder().build()) {
localNetwork.getRlpxAgent().subscribeConnectRequest((p, d) -> true);
remoteNetwork.getRlpxAgent().subscribeConnectRequest((p, d) -> true);
localNetwork.start(); localNetwork.start();
remoteNetwork.start(); remoteNetwork.start();
@ -262,6 +211,8 @@ public class P2PNetworkTest {
try (final P2PNetwork localNetwork = builder().peerPermissions(peerPermissions).build(); try (final P2PNetwork localNetwork = builder().peerPermissions(peerPermissions).build();
final P2PNetwork remoteNetwork = builder().build()) { final P2PNetwork remoteNetwork = builder().build()) {
localNetwork.getRlpxAgent().subscribeConnectRequest((p, d) -> true);
remoteNetwork.getRlpxAgent().subscribeConnectRequest((p, d) -> true);
localNetwork.start(); localNetwork.start();
remoteNetwork.start(); remoteNetwork.start();
@ -323,6 +274,8 @@ public class P2PNetworkTest {
.storageProvider(new InMemoryKeyValueStorageProvider()) .storageProvider(new InMemoryKeyValueStorageProvider())
.blockNumberForks(Collections.emptyList()) .blockNumberForks(Collections.emptyList())
.timestampForks(Collections.emptyList()) .timestampForks(Collections.emptyList())
.blockchain(blockchainMock); .blockchain(blockchainMock)
.allConnectionsSupplier(Stream::empty)
.allActiveConnectionsSupplier(Stream::empty);
} }
} }

@ -44,6 +44,7 @@ import org.hyperledger.besu.ethereum.p2p.rlpx.wire.AbstractMessageData;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.Capability; import org.hyperledger.besu.ethereum.p2p.rlpx.wire.Capability;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.Message; import org.hyperledger.besu.ethereum.p2p.rlpx.wire.Message;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.MockSubProtocol; import org.hyperledger.besu.ethereum.p2p.rlpx.wire.MockSubProtocol;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.ShouldConnectCallback;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.SubProtocol; import org.hyperledger.besu.ethereum.p2p.rlpx.wire.SubProtocol;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.messages.DisconnectMessage.DisconnectReason; import org.hyperledger.besu.ethereum.p2p.rlpx.wire.messages.DisconnectMessage.DisconnectReason;
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
@ -58,6 +59,7 @@ import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;
import io.vertx.core.Vertx; import io.vertx.core.Vertx;
import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes;
@ -89,6 +91,8 @@ public class P2PPlainNetworkTest {
final NodeKey nodeKey = NodeKeyUtils.generate(); final NodeKey nodeKey = NodeKeyUtils.generate();
try (final P2PNetwork listener = builder("partner1client1").nodeKey(nodeKey).build(); try (final P2PNetwork listener = builder("partner1client1").nodeKey(nodeKey).build();
final P2PNetwork connector = builder("partner2client1").build()) { final P2PNetwork connector = builder("partner2client1").build()) {
listener.getRlpxAgent().subscribeConnectRequest(testCallback);
connector.getRlpxAgent().subscribeConnectRequest(testCallback);
listener.start(); listener.start();
connector.start(); connector.start();
@ -111,6 +115,8 @@ public class P2PPlainNetworkTest {
final NodeKey listenNodeKey = NodeKeyUtils.generate(); final NodeKey listenNodeKey = NodeKeyUtils.generate();
try (final P2PNetwork listener = builder("partner1client1").nodeKey(listenNodeKey).build(); try (final P2PNetwork listener = builder("partner1client1").nodeKey(listenNodeKey).build();
final P2PNetwork connector = builder("partner2client1").build()) { final P2PNetwork connector = builder("partner2client1").build()) {
listener.getRlpxAgent().subscribeConnectRequest(testCallback);
connector.getRlpxAgent().subscribeConnectRequest(testCallback);
listener.start(); listener.start();
connector.start(); connector.start();
@ -142,19 +148,20 @@ public class P2PPlainNetworkTest {
@Test @Test
public void limitMaxPeers() throws Exception { public void limitMaxPeers() throws Exception {
final NodeKey nodeKey = NodeKeyUtils.generate(); final NodeKey nodeKey = NodeKeyUtils.generate();
final int maxPeers = 1;
final NetworkingConfiguration listenerConfig = final NetworkingConfiguration listenerConfig =
NetworkingConfiguration.create() NetworkingConfiguration.create()
.setDiscovery(DiscoveryConfiguration.create().setActive(false)) .setDiscovery(DiscoveryConfiguration.create().setActive(false))
.setRlpx( .setRlpx(
RlpxConfiguration.create() RlpxConfiguration.create()
.setBindPort(0) .setBindPort(0)
.setPeerUpperBound(maxPeers)
.setSupportedProtocols(MockSubProtocol.create())); .setSupportedProtocols(MockSubProtocol.create()));
try (final P2PNetwork listener = try (final P2PNetwork listener =
builder("partner1client1").nodeKey(nodeKey).config(listenerConfig).build(); builder("partner1client1").nodeKey(nodeKey).config(listenerConfig).build();
final P2PNetwork connector1 = builder("partner1client1").build(); final P2PNetwork connector1 = builder("partner1client1").build();
final P2PNetwork connector2 = builder("partner2client1").build()) { final P2PNetwork connector2 = builder("partner2client1").build()) {
listener.getRlpxAgent().subscribeConnectRequest(testCallback);
connector1.getRlpxAgent().subscribeConnectRequest(testCallback);
connector2.getRlpxAgent().subscribeConnectRequest((p, d) -> false);
// Setup listener and first connection // Setup listener and first connection
listener.start(); listener.start();
@ -181,17 +188,7 @@ public class P2PPlainNetworkTest {
reasonFuture.complete(reason); reasonFuture.complete(reason);
}); });
connector2.start(); connector2.start();
Assertions.assertThat( Assertions.assertThat(connector2.connect(listeningPeer)).isCompletedExceptionally();
connector2
.connect(listeningPeer)
.get(30L, TimeUnit.SECONDS)
.getPeerInfo()
.getNodeId())
.isEqualTo(listenId);
Assertions.assertThat(peerFuture.get(30L, TimeUnit.SECONDS).getPeerInfo().getNodeId())
.isEqualTo(listenId);
assertThat(reasonFuture.get(30L, TimeUnit.SECONDS))
.isEqualByComparingTo(DisconnectReason.TOO_MANY_PEERS);
} }
} }
@ -214,6 +211,9 @@ public class P2PPlainNetworkTest {
.nodeKey(connectorNodeKey) .nodeKey(connectorNodeKey)
.supportedCapabilities(cap2) .supportedCapabilities(cap2)
.build()) { .build()) {
listener.getRlpxAgent().subscribeConnectRequest(testCallback);
connector.getRlpxAgent().subscribeConnectRequest(testCallback);
listener.start(); listener.start();
connector.start(); connector.start();
final EnodeURL listenerEnode = listener.getLocalEnode().get(); final EnodeURL listenerEnode = listener.getLocalEnode().get();
@ -233,6 +233,8 @@ public class P2PPlainNetworkTest {
try (final P2PNetwork localNetwork = try (final P2PNetwork localNetwork =
builder("partner1client1").peerPermissions(localDenylist).build(); builder("partner1client1").peerPermissions(localDenylist).build();
final P2PNetwork remoteNetwork = builder("partner2client1").build()) { final P2PNetwork remoteNetwork = builder("partner2client1").build()) {
localNetwork.getRlpxAgent().subscribeConnectRequest(testCallback);
remoteNetwork.getRlpxAgent().subscribeConnectRequest(testCallback);
localNetwork.start(); localNetwork.start();
remoteNetwork.start(); remoteNetwork.start();
@ -281,6 +283,8 @@ public class P2PPlainNetworkTest {
try (final P2PNetwork localNetwork = try (final P2PNetwork localNetwork =
builder("partner1client1").peerPermissions(peerPermissions).build(); builder("partner1client1").peerPermissions(peerPermissions).build();
final P2PNetwork remoteNetwork = builder("partner2client1").build()) { final P2PNetwork remoteNetwork = builder("partner2client1").build()) {
localNetwork.getRlpxAgent().subscribeConnectRequest(testCallback);
remoteNetwork.getRlpxAgent().subscribeConnectRequest(testCallback);
localNetwork.start(); localNetwork.start();
remoteNetwork.start(); remoteNetwork.start();
@ -330,6 +334,8 @@ public class P2PPlainNetworkTest {
final NodeKey nodeKey = NodeKeyUtils.generate(); final NodeKey nodeKey = NodeKeyUtils.generate();
try (final P2PNetwork listener = builder("partner1client1").nodeKey(nodeKey).build(); try (final P2PNetwork listener = builder("partner1client1").nodeKey(nodeKey).build();
final P2PNetwork connector = builder("partner2client1").build()) { final P2PNetwork connector = builder("partner2client1").build()) {
listener.getRlpxAgent().subscribeConnectRequest(testCallback);
connector.getRlpxAgent().subscribeConnectRequest(testCallback);
final CompletableFuture<DisconnectReason> disconnectReasonFuture = new CompletableFuture<>(); final CompletableFuture<DisconnectReason> disconnectReasonFuture = new CompletableFuture<>();
listener.subscribeDisconnect( listener.subscribeDisconnect(
@ -378,6 +384,8 @@ public class P2PPlainNetworkTest {
} }
} }
private final ShouldConnectCallback testCallback = (p, d) -> true;
private static class LargeMessageData extends AbstractMessageData { private static class LargeMessageData extends AbstractMessageData {
public static final int VALID_ETH_MESSAGE_CODE = 0x07; public static final int VALID_ETH_MESSAGE_CODE = 0x07;
@ -410,7 +418,7 @@ public class P2PPlainNetworkTest {
private static Path toPath(final String path) { private static Path toPath(final String path) {
try { try {
return Path.of(Objects.requireNonNull(P2PPlainNetworkTest.class.getResource(path)).toURI()); return Path.of(Objects.requireNonNull(P2PPlainNetworkTest.class.getResource(path)).toURI());
} catch (URISyntaxException e) { } catch (final URISyntaxException e) {
throw new RuntimeException("Error converting to URI.", e); throw new RuntimeException("Error converting to URI.", e);
} }
} }
@ -449,6 +457,8 @@ public class P2PPlainNetworkTest {
.storageProvider(new InMemoryKeyValueStorageProvider()) .storageProvider(new InMemoryKeyValueStorageProvider())
.blockNumberForks(Collections.emptyList()) .blockNumberForks(Collections.emptyList())
.timestampForks(Collections.emptyList()) .timestampForks(Collections.emptyList())
.blockchain(blockchainMock); .blockchain(blockchainMock)
.allConnectionsSupplier(Stream::empty)
.allActiveConnectionsSupplier(Stream::empty);
} }
} }

@ -181,7 +181,8 @@ public class AbstractPeerConnectionTest {
connectionId, connectionId,
multiplexer, multiplexer,
connectionEventDispatcher, connectionEventDispatcher,
outboundMessagesCounter); outboundMessagesCounter,
true);
} }
@Override @Override

@ -44,7 +44,7 @@ public class MockConnectionInitializer implements ConnectionInitializer {
} }
public void completePendingFutures() { public void completePendingFutures() {
for (Map.Entry<Peer, CompletableFuture<PeerConnection>> conn : for (final Map.Entry<Peer, CompletableFuture<PeerConnection>> conn :
incompleteConnections.entrySet()) { incompleteConnections.entrySet()) {
conn.getValue().complete(MockPeerConnection.create(conn.getKey())); conn.getValue().complete(MockPeerConnection.create(conn.getKey()));
} }
@ -57,7 +57,7 @@ public class MockConnectionInitializer implements ConnectionInitializer {
@Override @Override
public CompletableFuture<InetSocketAddress> start() { public CompletableFuture<InetSocketAddress> start() {
InetSocketAddress socketAddress = final InetSocketAddress socketAddress =
new InetSocketAddress("127.0.0.1", NEXT_PORT.incrementAndGet()); new InetSocketAddress("127.0.0.1", NEXT_PORT.incrementAndGet());
return CompletableFuture.completedFuture(socketAddress); return CompletableFuture.completedFuture(socketAddress);
} }
@ -76,12 +76,14 @@ public class MockConnectionInitializer implements ConnectionInitializer {
public CompletableFuture<PeerConnection> connect(final Peer peer) { public CompletableFuture<PeerConnection> connect(final Peer peer) {
if (autoDisconnectCounter > 0) { if (autoDisconnectCounter > 0) {
autoDisconnectCounter--; autoDisconnectCounter--;
MockPeerConnection mockPeerConnection = MockPeerConnection.create(peer, eventDispatcher); final MockPeerConnection mockPeerConnection =
MockPeerConnection.create(peer, eventDispatcher, false);
mockPeerConnection.disconnect(DisconnectMessage.DisconnectReason.CLIENT_QUITTING); mockPeerConnection.disconnect(DisconnectMessage.DisconnectReason.CLIENT_QUITTING);
return CompletableFuture.completedFuture(mockPeerConnection); return CompletableFuture.completedFuture(mockPeerConnection);
} }
if (autocompleteConnections) { if (autocompleteConnections) {
return CompletableFuture.completedFuture(MockPeerConnection.create(peer, eventDispatcher)); return CompletableFuture.completedFuture(
MockPeerConnection.create(peer, eventDispatcher, false));
} else { } else {
final CompletableFuture<PeerConnection> future = new CompletableFuture<>(); final CompletableFuture<PeerConnection> future = new CompletableFuture<>();
incompleteConnections.put(peer, future); incompleteConnections.put(peer, future);

@ -47,7 +47,8 @@ public class MockPeerConnection extends AbstractPeerConnection {
final String connectionId, final String connectionId,
final CapabilityMultiplexer multiplexer, final CapabilityMultiplexer multiplexer,
final PeerConnectionEventDispatcher connectionEventDispatcher, final PeerConnectionEventDispatcher connectionEventDispatcher,
final LabelledMetric<Counter> outboundMessagesCounter) { final LabelledMetric<Counter> outboundMessagesCounter,
final boolean inboundInitiated) {
super( super(
peer, peer,
peerInfo, peerInfo,
@ -56,7 +57,8 @@ public class MockPeerConnection extends AbstractPeerConnection {
connectionId, connectionId,
multiplexer, multiplexer,
connectionEventDispatcher, connectionEventDispatcher,
outboundMessagesCounter); outboundMessagesCounter,
inboundInitiated);
} }
public static MockPeerConnection create() { public static MockPeerConnection create() {
@ -64,11 +66,13 @@ public class MockPeerConnection extends AbstractPeerConnection {
} }
public static MockPeerConnection create(final Peer peer) { public static MockPeerConnection create(final Peer peer) {
return create(peer, mock(PeerConnectionEventDispatcher.class)); return create(peer, mock(PeerConnectionEventDispatcher.class), true);
} }
public static MockPeerConnection create( public static MockPeerConnection create(
final Peer peer, final PeerConnectionEventDispatcher eventDispatcher) { final Peer peer,
final PeerConnectionEventDispatcher eventDispatcher,
final boolean inboundInitiated) {
final List<SubProtocol> subProtocols = Arrays.asList(MockSubProtocol.create("eth")); final List<SubProtocol> subProtocols = Arrays.asList(MockSubProtocol.create("eth"));
final List<Capability> caps = Arrays.asList(Capability.create("eth", 63)); final List<Capability> caps = Arrays.asList(Capability.create("eth", 63));
final CapabilityMultiplexer multiplexer = new CapabilityMultiplexer(subProtocols, caps, caps); final CapabilityMultiplexer multiplexer = new CapabilityMultiplexer(subProtocols, caps, caps);
@ -83,7 +87,8 @@ public class MockPeerConnection extends AbstractPeerConnection {
Integer.toString(connectionId.incrementAndGet()), Integer.toString(connectionId.incrementAndGet()),
multiplexer, multiplexer,
eventDispatcher, eventDispatcher,
NoOpMetricsSystem.NO_OP_LABELLED_3_COUNTER); NoOpMetricsSystem.NO_OP_LABELLED_3_COUNTER,
inboundInitiated);
} }
@Override @Override

@ -1,269 +0,0 @@
/*
* Copyright ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.p2p.rlpx.connections;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.hyperledger.besu.ethereum.p2p.peers.PeerTestHelper.createPeer;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import org.hyperledger.besu.ethereum.p2p.peers.Peer;
import org.hyperledger.besu.ethereum.p2p.rlpx.connections.RlpxConnection.ConnectionNotEstablishedException;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.messages.DisconnectMessage.DisconnectReason;
import java.util.concurrent.CompletableFuture;
import org.junit.Test;
public class RlpxConnectionTest {
@Test
public void getPeer_pendingOutboundConnection() {
final Peer peer = createPeer();
final CompletableFuture<PeerConnection> future = new CompletableFuture<>();
final RlpxConnection conn = RlpxConnection.outboundConnection(peer, future);
assertThat(conn.getPeer()).isEqualTo(peer);
}
@Test
public void getPeer_establishedOutboundConnection() {
final Peer peer = createPeer();
final CompletableFuture<PeerConnection> future = new CompletableFuture<>();
final RlpxConnection conn = RlpxConnection.outboundConnection(peer, future);
future.complete(peerConnection(peer));
assertThat(conn.getPeer()).isEqualTo(peer);
}
@Test
public void getPeer_inboundConnection() {
final Peer peer = createPeer();
final PeerConnection peerConnection = peerConnection(peer);
final RlpxConnection conn = RlpxConnection.inboundConnection(peerConnection);
assertThat(conn.getPeer()).isEqualTo(peer);
}
@Test
public void disconnect_pendingOutboundConnection() {
final Peer peer = createPeer();
final CompletableFuture<PeerConnection> future = new CompletableFuture<>();
final RlpxConnection conn = RlpxConnection.outboundConnection(peer, future);
final DisconnectReason reason = DisconnectReason.REQUESTED;
conn.disconnect(reason);
assertThat(conn.isFailedOrDisconnected()).isFalse();
// Resolve future
final PeerConnection peerConnection = peerConnection(peer);
future.complete(peerConnection);
// Check disconnect was issued
verify(peerConnection).disconnect(reason);
assertThat(conn.isFailedOrDisconnected()).isTrue();
}
@Test
public void disconnect_activeOutboundConnection() {
final Peer peer = createPeer();
final CompletableFuture<PeerConnection> future = new CompletableFuture<>();
final RlpxConnection conn = RlpxConnection.outboundConnection(peer, future);
final PeerConnection peerConnection = peerConnection(peer);
future.complete(peerConnection);
final DisconnectReason reason = DisconnectReason.REQUESTED;
conn.disconnect(reason);
// Check disconnect was issued
assertThat(conn.isFailedOrDisconnected()).isTrue();
verify(peerConnection).disconnect(reason);
}
@Test
public void disconnect_failedOutboundConnection() {
final Peer peer = createPeer();
final CompletableFuture<PeerConnection> future = new CompletableFuture<>();
final RlpxConnection conn = RlpxConnection.outboundConnection(peer, future);
future.completeExceptionally(new IllegalStateException("whoops"));
assertThat(conn.isFailedOrDisconnected()).isTrue();
final DisconnectReason reason = DisconnectReason.REQUESTED;
conn.disconnect(reason);
assertThat(conn.isFailedOrDisconnected()).isTrue();
}
@Test
public void disconnect_inboundConnection() {
final Peer peer = createPeer();
final PeerConnection peerConnection = peerConnection(peer);
final RlpxConnection conn = RlpxConnection.inboundConnection(peerConnection);
assertThat(conn.isFailedOrDisconnected()).isFalse();
final DisconnectReason reason = DisconnectReason.REQUESTED;
conn.disconnect(reason);
// Check disconnect was issued
assertThat(conn.isFailedOrDisconnected()).isTrue();
verify(peerConnection).disconnect(reason);
}
@Test
public void getPeerConnection_pendingOutboundConnection() {
final Peer peer = createPeer();
final CompletableFuture<PeerConnection> future = new CompletableFuture<>();
final RlpxConnection conn = RlpxConnection.outboundConnection(peer, future);
assertThatThrownBy(conn::getPeerConnection)
.isInstanceOf(ConnectionNotEstablishedException.class);
}
@Test
public void getPeerConnection_activeOutboundConnection() {
final Peer peer = createPeer();
final CompletableFuture<PeerConnection> future = new CompletableFuture<>();
final RlpxConnection conn = RlpxConnection.outboundConnection(peer, future);
final PeerConnection peerConnection = peerConnection(peer);
future.complete(peerConnection);
assertThat(conn.getPeerConnection()).isEqualTo(peerConnection);
}
@Test
public void getPeerConnection_failedOutboundConnection() {
final Peer peer = createPeer();
final CompletableFuture<PeerConnection> future = new CompletableFuture<>();
final RlpxConnection conn = RlpxConnection.outboundConnection(peer, future);
future.completeExceptionally(new IllegalStateException("whoops"));
assertThatThrownBy(conn::getPeerConnection)
.isInstanceOf(ConnectionNotEstablishedException.class);
}
@Test
public void getPeerConnection_disconnectedOutboundConnection() {
final Peer peer = createPeer();
final CompletableFuture<PeerConnection> future = new CompletableFuture<>();
final RlpxConnection conn = RlpxConnection.outboundConnection(peer, future);
final PeerConnection peerConnection = peerConnection(peer);
future.complete(peerConnection);
conn.disconnect(DisconnectReason.REQUESTED);
assertThat(conn.getPeerConnection()).isEqualTo(peerConnection);
}
@Test
public void getPeerConnection_activeInboundConnection() {
final Peer peer = createPeer();
final PeerConnection peerConnection = peerConnection(peer);
final RlpxConnection conn = RlpxConnection.inboundConnection(peerConnection);
assertThat(conn.getPeerConnection()).isEqualTo(peerConnection);
}
@Test
public void getPeerConnection_disconnectedInboundConnection() {
final Peer peer = createPeer();
final PeerConnection peerConnection = peerConnection(peer);
final RlpxConnection conn = RlpxConnection.inboundConnection(peerConnection);
conn.disconnect(DisconnectReason.REQUESTED);
assertThat(conn.getPeerConnection()).isEqualTo(peerConnection);
}
@Test
public void checkState_pendingOutboundConnection() {
final Peer peer = createPeer();
final CompletableFuture<PeerConnection> future = new CompletableFuture<>();
final RlpxConnection conn = RlpxConnection.outboundConnection(peer, future);
assertThat(conn.initiatedRemotely()).isFalse();
assertThat(conn.isActive()).isFalse();
assertThat(conn.isPending()).isTrue();
assertThat(conn.isFailedOrDisconnected()).isFalse();
}
@Test
public void checkState_activeOutboundConnection() {
final Peer peer = createPeer();
final CompletableFuture<PeerConnection> future = new CompletableFuture<>();
final RlpxConnection conn = RlpxConnection.outboundConnection(peer, future);
final PeerConnection peerConnection = peerConnection(peer);
future.complete(peerConnection);
assertThat(conn.initiatedRemotely()).isFalse();
assertThat(conn.isActive()).isTrue();
assertThat(conn.isPending()).isFalse();
assertThat(conn.isFailedOrDisconnected()).isFalse();
}
@Test
public void checkState_failedOutboundConnection() {
final Peer peer = createPeer();
final CompletableFuture<PeerConnection> future = new CompletableFuture<>();
final RlpxConnection conn = RlpxConnection.outboundConnection(peer, future);
future.completeExceptionally(new IllegalStateException("whoops"));
assertThat(conn.initiatedRemotely()).isFalse();
assertThat(conn.isActive()).isFalse();
assertThat(conn.isPending()).isFalse();
assertThat(conn.isFailedOrDisconnected()).isTrue();
}
@Test
public void checkState_disconnectedOutboundConnection() {
final Peer peer = createPeer();
final CompletableFuture<PeerConnection> future = new CompletableFuture<>();
final RlpxConnection conn = RlpxConnection.outboundConnection(peer, future);
final PeerConnection peerConnection = peerConnection(peer);
future.complete(peerConnection);
conn.disconnect(DisconnectReason.UNKNOWN);
assertThat(conn.initiatedRemotely()).isFalse();
assertThat(conn.isActive()).isFalse();
assertThat(conn.isPending()).isFalse();
assertThat(conn.isFailedOrDisconnected()).isTrue();
}
@Test
public void checkState_activeInboundConnection() {
final Peer peer = createPeer();
final PeerConnection peerConnection = peerConnection(peer);
final RlpxConnection conn = RlpxConnection.inboundConnection(peerConnection);
assertThat(conn.initiatedRemotely()).isTrue();
assertThat(conn.isActive()).isTrue();
assertThat(conn.isPending()).isFalse();
assertThat(conn.isFailedOrDisconnected()).isFalse();
}
@Test
public void checkState_disconnectedInboundConnection() {
final Peer peer = createPeer();
final PeerConnection peerConnection = peerConnection(peer);
final RlpxConnection conn = RlpxConnection.inboundConnection(peerConnection);
conn.disconnect(DisconnectReason.UNKNOWN);
assertThat(conn.initiatedRemotely()).isTrue();
assertThat(conn.isActive()).isFalse();
assertThat(conn.isPending()).isFalse();
assertThat(conn.isFailedOrDisconnected()).isTrue();
}
private PeerConnection peerConnection(final Peer peer) {
return spy(MockPeerConnection.create(peer));
}
}

@ -421,6 +421,7 @@ public class DeFramerTest {
Optional.ofNullable(expectedPeer), Optional.ofNullable(expectedPeer),
connectionEventDispatcher, connectionEventDispatcher,
connectFuture, connectFuture,
new NoOpMetricsSystem()); new NoOpMetricsSystem(),
true);
} }
} }

@ -63,7 +63,7 @@ public class InsufficientPeersPermissioningProvider implements ContextualNodePer
@Override @Override
public Optional<Boolean> isPermitted( public Optional<Boolean> isPermitted(
final EnodeURL sourceEnode, final EnodeURL destinationEnode) { final EnodeURL sourceEnode, final EnodeURL destinationEnode) {
Optional<EnodeURL> maybeSelfEnode = p2pNetwork.getLocalEnode(); final Optional<EnodeURL> maybeSelfEnode = p2pNetwork.getLocalEnode();
if (nonBootnodePeerConnections > 0) { if (nonBootnodePeerConnections > 0) {
return Optional.empty(); return Optional.empty();
} else if (!maybeSelfEnode.isPresent()) { } else if (!maybeSelfEnode.isPresent()) {

@ -183,21 +183,21 @@ public class InsufficientPeersPermissioningProviderTest {
ArgumentCaptor.forClass(ConnectCallback.class); ArgumentCaptor.forClass(ConnectCallback.class);
verify(p2pNetwork).subscribeConnect(callbackCaptor.capture()); verify(p2pNetwork).subscribeConnect(callbackCaptor.capture());
final ConnectCallback connectCallback = callbackCaptor.getValue(); final ConnectCallback incomingConnectCallback = callbackCaptor.getValue();
final Runnable updatePermsCallback = mock(Runnable.class); final Runnable updatePermsCallback = mock(Runnable.class);
provider.subscribeToUpdates(updatePermsCallback); provider.subscribeToUpdates(updatePermsCallback);
connectCallback.onConnect(peerConnectionMatching(ENODE_2)); incomingConnectCallback.onConnect(peerConnectionMatching(ENODE_2));
verify(updatePermsCallback, times(0)).run(); verify(updatePermsCallback, times(0)).run();
connectCallback.onConnect(peerConnectionMatching(ENODE_3)); incomingConnectCallback.onConnect(peerConnectionMatching(ENODE_3));
verify(updatePermsCallback, times(0)).run(); verify(updatePermsCallback, times(0)).run();
connectCallback.onConnect(peerConnectionMatching(ENODE_4)); incomingConnectCallback.onConnect(peerConnectionMatching(ENODE_4));
verify(updatePermsCallback, times(1)).run(); verify(updatePermsCallback, times(1)).run();
connectCallback.onConnect(peerConnectionMatching(ENODE_5)); incomingConnectCallback.onConnect(peerConnectionMatching(ENODE_5));
verify(updatePermsCallback, times(1)).run(); verify(updatePermsCallback, times(1)).run();
connectCallback.onConnect(peerConnectionMatching(ENODE_3)); incomingConnectCallback.onConnect(peerConnectionMatching(ENODE_3));
verify(updatePermsCallback, times(1)).run(); verify(updatePermsCallback, times(1)).run();
} }
@ -215,7 +215,7 @@ public class InsufficientPeersPermissioningProviderTest {
final ArgumentCaptor<ConnectCallback> connectCallbackCaptor = final ArgumentCaptor<ConnectCallback> connectCallbackCaptor =
ArgumentCaptor.forClass(ConnectCallback.class); ArgumentCaptor.forClass(ConnectCallback.class);
verify(p2pNetwork).subscribeConnect(connectCallbackCaptor.capture()); verify(p2pNetwork).subscribeConnect(connectCallbackCaptor.capture());
final ConnectCallback connectCallback = connectCallbackCaptor.getValue(); final ConnectCallback incomingConnectCallback = connectCallbackCaptor.getValue();
final ArgumentCaptor<DisconnectCallback> disconnectCallbackCaptor = final ArgumentCaptor<DisconnectCallback> disconnectCallbackCaptor =
ArgumentCaptor.forClass(DisconnectCallback.class); ArgumentCaptor.forClass(DisconnectCallback.class);
@ -226,13 +226,13 @@ public class InsufficientPeersPermissioningProviderTest {
provider.subscribeToUpdates(updatePermsCallback); provider.subscribeToUpdates(updatePermsCallback);
connectCallback.onConnect(peerConnectionMatching(ENODE_2)); incomingConnectCallback.onConnect(peerConnectionMatching(ENODE_2));
verify(updatePermsCallback, times(0)).run(); verify(updatePermsCallback, times(0)).run();
connectCallback.onConnect(peerConnectionMatching(ENODE_3)); incomingConnectCallback.onConnect(peerConnectionMatching(ENODE_3));
verify(updatePermsCallback, times(0)).run(); verify(updatePermsCallback, times(0)).run();
connectCallback.onConnect(peerConnectionMatching(ENODE_4)); incomingConnectCallback.onConnect(peerConnectionMatching(ENODE_4));
verify(updatePermsCallback, times(1)).run(); verify(updatePermsCallback, times(1)).run();
connectCallback.onConnect(peerConnectionMatching(ENODE_5)); incomingConnectCallback.onConnect(peerConnectionMatching(ENODE_5));
verify(updatePermsCallback, times(1)).run(); verify(updatePermsCallback, times(1)).run();
disconnectCallback.onDisconnect(peerConnectionMatching(ENODE_2), null, true); disconnectCallback.onDisconnect(peerConnectionMatching(ENODE_2), null, true);
verify(updatePermsCallback, times(1)).run(); verify(updatePermsCallback, times(1)).run();

@ -65,6 +65,7 @@ import org.hyperledger.besu.plugin.services.MetricsSystem;
import org.hyperledger.besu.services.kvstore.InMemoryKeyValueStorage; import org.hyperledger.besu.services.kvstore.InMemoryKeyValueStorage;
import org.hyperledger.besu.util.Subscribers; import org.hyperledger.besu.util.Subscribers;
import java.util.Collections;
import java.util.Optional; import java.util.Optional;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Supplier; import java.util.function.Supplier;
@ -83,6 +84,7 @@ public class RetestethContext {
private static final PoWHasher NO_WORK_HASHER = private static final PoWHasher NO_WORK_HASHER =
(final long nonce, final long number, EpochCalculator epochCalc, final Bytes headerHash) -> (final long nonce, final long number, EpochCalculator epochCalc, final Bytes headerHash) ->
new PoWSolution(nonce, Hash.ZERO, UInt256.ZERO, Hash.ZERO); new PoWSolution(nonce, Hash.ZERO, UInt256.ZERO, Hash.ZERO);
public static final int MAX_PEERS = 25;
private final ReentrantLock contextLock = new ReentrantLock(); private final ReentrantLock contextLock = new ReentrantLock();
private Address coinbase; private Address coinbase;
@ -198,6 +200,8 @@ public class RetestethContext {
blockReplay = new BlockReplay(protocolSchedule, blockchainQueries.getBlockchain()); blockReplay = new BlockReplay(protocolSchedule, blockchainQueries.getBlockchain());
final Bytes localNodeKey = Bytes.wrap(new byte[64]);
// mining support // mining support
final Supplier<ProtocolSpec> currentProtocolSpecSupplier = final Supplier<ProtocolSpec> currentProtocolSpecSupplier =
@ -208,8 +212,13 @@ public class RetestethContext {
currentProtocolSpecSupplier, currentProtocolSpecSupplier,
retestethClock, retestethClock,
metricsSystem, metricsSystem,
0, EthProtocolConfiguration.DEFAULT_MAX_MESSAGE_SIZE,
EthProtocolConfiguration.DEFAULT_MAX_MESSAGE_SIZE); Collections.emptyList(),
localNodeKey,
MAX_PEERS,
MAX_PEERS,
MAX_PEERS,
false);
final SyncState syncState = new SyncState(blockchain, ethPeers); final SyncState syncState = new SyncState(blockchain, ethPeers);
ethScheduler = new EthScheduler(1, 1, 1, 1, metricsSystem); ethScheduler = new EthScheduler(1, 1, 1, 1, metricsSystem);

@ -131,8 +131,7 @@ public class Subscribers<T> {
action.accept(subscriber); action.accept(subscriber);
} catch (final Exception e) { } catch (final Exception e) {
if (suppressCallbackExceptions) { if (suppressCallbackExceptions) {
LOG.info("Error in callback: {}", e.getMessage()); LOG.debug("Error in callback: {}", e);
LOG.debug("Error in callback: ", e);
} else { } else {
throw e; throw e;
} }

Loading…
Cancel
Save