mirror of
https://github.com/vacp2p/linea-besu.git
synced 2026-01-08 20:47:59 -05:00
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>
This commit is contained in:
@@ -168,7 +168,6 @@ public class ThreadBesuNodeRunner implements BesuNodeRunner {
|
||||
.build();
|
||||
|
||||
final int maxPeers = 25;
|
||||
final int minPeers = 25;
|
||||
|
||||
builder
|
||||
.synchronizerConfiguration(new SynchronizerConfiguration.Builder().build())
|
||||
@@ -187,7 +186,11 @@ public class ThreadBesuNodeRunner implements BesuNodeRunner {
|
||||
node.getPkiKeyStoreConfiguration()
|
||||
.map(pkiConfig -> new PkiBlockCreationConfigurationProvider().load(pkiConfig)))
|
||||
.evmConfiguration(EvmConfiguration.DEFAULT)
|
||||
.maxPeers(maxPeers);
|
||||
.maxPeers(maxPeers)
|
||||
.lowerBoundPeers(maxPeers)
|
||||
.maxRemotelyInitiatedPeers(15)
|
||||
.networkConfiguration(node.getNetworkingConfiguration())
|
||||
.randomPeerPriority(false);
|
||||
|
||||
node.getGenesisConfig()
|
||||
.map(GenesisConfigFile::fromConfig)
|
||||
@@ -205,8 +208,6 @@ public class ThreadBesuNodeRunner implements BesuNodeRunner {
|
||||
.discovery(node.isDiscoveryEnabled())
|
||||
.p2pAdvertisedHost(node.getHostName())
|
||||
.p2pListenPort(0)
|
||||
.maxPeers(maxPeers)
|
||||
.minPeers(minPeers)
|
||||
.networkingConfiguration(node.getNetworkingConfiguration())
|
||||
.jsonRpcConfiguration(node.jsonRpcConfiguration())
|
||||
.webSocketConfiguration(node.webSocketConfiguration())
|
||||
|
||||
@@ -412,7 +412,7 @@ public class BesuNodeConfigurationBuilder {
|
||||
.withCrlPath(toPath(crl));
|
||||
break;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
} catch (final Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
this.tlsConfiguration = Optional.of(builder.build());
|
||||
|
||||
@@ -54,7 +54,7 @@ public class TestPermissioningPlugin implements BesuPlugin {
|
||||
if (sourceEnode.toString().contains(bobNode)
|
||||
|| destinationEnode.toString().contains(bobNode)) {
|
||||
|
||||
boolean isBobTalkingToAlice =
|
||||
final boolean isBobTalkingToAlice =
|
||||
sourceEnode.toString().contains(aliceNode)
|
||||
|| destinationEnode.toString().contains(aliceNode);
|
||||
if (isBobTalkingToAlice) {
|
||||
|
||||
@@ -128,7 +128,7 @@ public class OpenTelemetryAcceptanceTest extends AcceptanceTestBase {
|
||||
@BeforeEach
|
||||
public void setUp() throws Exception {
|
||||
System.setProperty("root.log.level", "DEBUG");
|
||||
Server server =
|
||||
final Server server =
|
||||
NettyServerBuilder.forPort(4317)
|
||||
.addService(fakeTracesCollector)
|
||||
.addService(fakeMetricsCollector)
|
||||
@@ -136,14 +136,14 @@ public class OpenTelemetryAcceptanceTest extends AcceptanceTestBase {
|
||||
.start();
|
||||
closer.register(server::shutdownNow);
|
||||
|
||||
MetricsConfiguration configuration =
|
||||
final MetricsConfiguration configuration =
|
||||
MetricsConfiguration.builder()
|
||||
.protocol(MetricsProtocol.OPENTELEMETRY)
|
||||
.enabled(true)
|
||||
.port(0)
|
||||
.hostsAllowlist(singletonList("*"))
|
||||
.build();
|
||||
Map<String, String> env = new HashMap<>();
|
||||
final Map<String, String> env = new HashMap<>();
|
||||
env.put("OTEL_METRIC_EXPORT_INTERVAL", "1000");
|
||||
env.put("OTEL_TRACES_SAMPLER", "always_on");
|
||||
env.put("OTEL_EXPORTER_OTLP_ENDPOINT", "http://localhost:4317");
|
||||
@@ -173,7 +173,7 @@ public class OpenTelemetryAcceptanceTest extends AcceptanceTestBase {
|
||||
WaitUtils.waitFor(
|
||||
30,
|
||||
() -> {
|
||||
List<ResourceMetrics> resourceMetrics = fakeMetricsCollector.getReceivedMetrics();
|
||||
final List<ResourceMetrics> resourceMetrics = fakeMetricsCollector.getReceivedMetrics();
|
||||
assertThat(resourceMetrics.isEmpty()).isFalse();
|
||||
});
|
||||
}
|
||||
@@ -186,26 +186,26 @@ public class OpenTelemetryAcceptanceTest extends AcceptanceTestBase {
|
||||
() -> {
|
||||
// call the json RPC endpoint to generate a trace.
|
||||
net.netVersion().verify(metricsNode);
|
||||
List<ResourceSpans> spans = fakeTracesCollector.getReceivedSpans();
|
||||
final List<ResourceSpans> spans = fakeTracesCollector.getReceivedSpans();
|
||||
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);
|
||||
ByteString parent = internalSpan.getParentSpanId();
|
||||
final ByteString parent = internalSpan.getParentSpanId();
|
||||
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);
|
||||
ByteString rootSpanId = serverSpan.getParentSpanId();
|
||||
final ByteString rootSpanId = serverSpan.getParentSpanId();
|
||||
assertThat(rootSpanId.isEmpty()).isTrue();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void traceReportingWithTraceId() {
|
||||
Duration timeout = Duration.ofSeconds(1);
|
||||
final Duration timeout = Duration.ofSeconds(1);
|
||||
WaitUtils.waitFor(
|
||||
60,
|
||||
() -> {
|
||||
OpenTelemetry openTelemetry =
|
||||
final OpenTelemetry openTelemetry =
|
||||
OpenTelemetrySdk.builder()
|
||||
.setPropagators(
|
||||
ContextPropagators.create(
|
||||
@@ -213,7 +213,7 @@ public class OpenTelemetryAcceptanceTest extends AcceptanceTestBase {
|
||||
.setTracerProvider(
|
||||
SdkTracerProvider.builder().setSampler(Sampler.alwaysOn()).build())
|
||||
.build();
|
||||
Call.Factory client =
|
||||
final Call.Factory client =
|
||||
OkHttpTelemetry.builder(openTelemetry)
|
||||
.build()
|
||||
.newCallFactory(
|
||||
@@ -222,7 +222,7 @@ public class OpenTelemetryAcceptanceTest extends AcceptanceTestBase {
|
||||
.readTimeout(timeout)
|
||||
.writeTimeout(timeout)
|
||||
.build());
|
||||
Request request =
|
||||
final Request request =
|
||||
new Request.Builder()
|
||||
.url("http://localhost:" + metricsNode.getJsonRpcPort().get())
|
||||
.post(
|
||||
@@ -230,18 +230,19 @@ public class OpenTelemetryAcceptanceTest extends AcceptanceTestBase {
|
||||
"{\"jsonrpc\":\"2.0\",\"method\":\"net_version\",\"params\":[],\"id\":255}",
|
||||
MediaType.get("application/json")))
|
||||
.build();
|
||||
Response response = client.newCall(request).execute();
|
||||
final Response response = client.newCall(request).execute();
|
||||
try {
|
||||
assertThat(response.code()).isEqualTo(200);
|
||||
List<ResourceSpans> spans = new ArrayList<>(fakeTracesCollector.getReceivedSpans());
|
||||
final List<ResourceSpans> spans =
|
||||
new ArrayList<>(fakeTracesCollector.getReceivedSpans());
|
||||
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);
|
||||
ByteString parent = internalSpan.getParentSpanId();
|
||||
final ByteString parent = internalSpan.getParentSpanId();
|
||||
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);
|
||||
ByteString rootSpanId = serverSpan.getParentSpanId();
|
||||
final ByteString rootSpanId = serverSpan.getParentSpanId();
|
||||
assertThat(rootSpanId.isEmpty()).isFalse();
|
||||
} finally {
|
||||
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.PrivacyParameters;
|
||||
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.mainnet.ProtocolSchedule;
|
||||
import org.hyperledger.besu.ethereum.mainnet.precompiles.privacy.FlexiblePrivacyPrecompiledContract;
|
||||
@@ -168,10 +169,6 @@ public class RunnerBuilder {
|
||||
private NatMethod natMethod = NatMethod.AUTO;
|
||||
private String natManagerServiceName;
|
||||
private boolean natMethodFallbackEnabled;
|
||||
private int maxPeers;
|
||||
private int minPeers;
|
||||
private boolean limitRemoteWireConnectionsEnabled = false;
|
||||
private float fractionRemoteConnectionsAllowed;
|
||||
private EthNetworkConfig ethNetworkConfig;
|
||||
private EthstatsOptions ethstatsOptions;
|
||||
private JsonRpcConfiguration jsonRpcConfiguration;
|
||||
@@ -189,7 +186,6 @@ public class RunnerBuilder {
|
||||
private Optional<String> identityString = Optional.empty();
|
||||
private BesuPluginContextImpl besuPluginContext;
|
||||
private boolean autoLogBloomCaching = true;
|
||||
private boolean randomPeerPriority;
|
||||
private StorageProvider storageProvider;
|
||||
private RpcEndpointServiceImpl rpcEndpointServiceImpl;
|
||||
private JsonRpcIpcConfiguration jsonRpcIpcConfiguration;
|
||||
@@ -354,52 +350,6 @@ public class RunnerBuilder {
|
||||
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
|
||||
*
|
||||
@@ -684,12 +634,8 @@ public class RunnerBuilder {
|
||||
RlpxConfiguration.create()
|
||||
.setBindHost(p2pListenInterface)
|
||||
.setBindPort(p2pListenPort)
|
||||
.setPeerUpperBound(maxPeers)
|
||||
.setPeerLowerBound(minPeers)
|
||||
.setSupportedProtocols(subProtocols)
|
||||
.setClientId(BesuInfo.nodeName(identityString))
|
||||
.setLimitRemoteWireConnectionsEnabled(limitRemoteWireConnectionsEnabled)
|
||||
.setFractionRemoteWireConnectionsAllowed(fractionRemoteConnectionsAllowed);
|
||||
.setClientId(BesuInfo.nodeName(identityString));
|
||||
networkingConfiguration.setRlpx(rlpxConfiguration).setDiscovery(discoveryConfiguration);
|
||||
|
||||
final PeerPermissionsDenylist bannedNodes = PeerPermissionsDenylist.create();
|
||||
@@ -719,23 +665,27 @@ public class RunnerBuilder {
|
||||
final NatService natService = new NatService(buildNatManager(natMethod), fallbackEnabled);
|
||||
final NetworkBuilder inactiveNetwork = caps -> new NoopP2PNetwork();
|
||||
final NetworkBuilder activeNetwork =
|
||||
caps ->
|
||||
DefaultP2PNetwork.builder()
|
||||
.vertx(vertx)
|
||||
.nodeKey(nodeKey)
|
||||
.config(networkingConfiguration)
|
||||
.legacyForkIdEnabled(legacyForkIdEnabled)
|
||||
.peerPermissions(peerPermissions)
|
||||
.metricsSystem(metricsSystem)
|
||||
.supportedCapabilities(caps)
|
||||
.natService(natService)
|
||||
.randomPeerPriority(randomPeerPriority)
|
||||
.storageProvider(storageProvider)
|
||||
.p2pTLSConfiguration(p2pTLSConfiguration)
|
||||
.blockchain(context.getBlockchain())
|
||||
.blockNumberForks(besuController.getGenesisConfigOptions().getForkBlockNumbers())
|
||||
.timestampForks(besuController.getGenesisConfigOptions().getForkBlockTimestamps())
|
||||
.build();
|
||||
caps -> {
|
||||
final EthPeers ethPeers = besuController.getEthPeers();
|
||||
return DefaultP2PNetwork.builder()
|
||||
.vertx(vertx)
|
||||
.nodeKey(nodeKey)
|
||||
.config(networkingConfiguration)
|
||||
.legacyForkIdEnabled(legacyForkIdEnabled)
|
||||
.peerPermissions(peerPermissions)
|
||||
.metricsSystem(metricsSystem)
|
||||
.supportedCapabilities(caps)
|
||||
.natService(natService)
|
||||
.storageProvider(storageProvider)
|
||||
.p2pTLSConfiguration(p2pTLSConfiguration)
|
||||
.blockchain(context.getBlockchain())
|
||||
.blockNumberForks(besuController.getGenesisConfigOptions().getForkBlockNumbers())
|
||||
.timestampForks(besuController.getGenesisConfigOptions().getForkBlockTimestamps())
|
||||
.allConnectionsSupplier(ethPeers::getAllConnections)
|
||||
.allActiveConnectionsSupplier(ethPeers::getAllActiveConnections)
|
||||
.peersLowerBound(ethPeers.getPeerLowerBound())
|
||||
.build();
|
||||
};
|
||||
|
||||
final NetworkRunner networkRunner =
|
||||
NetworkRunner.builder()
|
||||
@@ -745,6 +695,8 @@ public class RunnerBuilder {
|
||||
.metricsSystem(metricsSystem)
|
||||
.build();
|
||||
|
||||
besuController.getEthPeers().setRlpxAgent(networkRunner.getRlpxAgent());
|
||||
|
||||
final P2PNetwork network = networkRunner.getNetwork();
|
||||
// ForkId in Ethereum Node Record needs updating when we transition to a new protocol spec
|
||||
context
|
||||
@@ -1423,26 +1375,6 @@ public class RunnerBuilder {
|
||||
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.
|
||||
*
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package org.hyperledger.besu.cli;
|
||||
|
||||
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.util.Arrays.asList;
|
||||
import static java.util.Collections.singletonList;
|
||||
@@ -320,6 +321,10 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
|
||||
|
||||
private RocksDBPlugin rocksDBPlugin;
|
||||
|
||||
private int maxPeers;
|
||||
private int maxRemoteInitiatedPeers;
|
||||
private int peersLowerBound;
|
||||
|
||||
// CLI options defined by user at runtime.
|
||||
// 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})")
|
||||
private final Integer maxPeers = DEFAULT_MAX_PEERS;
|
||||
|
||||
private int minPeers;
|
||||
|
||||
@Option(
|
||||
names = {"--remote-connections-limit-enabled"},
|
||||
description =
|
||||
@@ -464,7 +467,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
|
||||
names = {"--random-peer-priority-enabled"},
|
||||
description =
|
||||
"Allow for incoming connections to be prioritized randomly. This will prevent (typically small, stable) networks from forming impenetrable peer cliques. (default: ${DEFAULT-VALUE})")
|
||||
private final Boolean randomPeerPriority = false;
|
||||
private final Boolean randomPeerPriority = Boolean.FALSE;
|
||||
|
||||
@Option(
|
||||
names = {"--banned-node-ids", "--banned-node-id"},
|
||||
@@ -1489,7 +1492,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
|
||||
validateOptions();
|
||||
configure();
|
||||
configureNativeLibs();
|
||||
initController();
|
||||
besuController = initController();
|
||||
|
||||
besuPluginContext.beforeExternalServices();
|
||||
|
||||
@@ -1686,8 +1689,6 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
|
||||
p2pTLSConfiguration,
|
||||
p2PDiscoveryOptionGroup.peerDiscoveryEnabled,
|
||||
ethNetworkConfig,
|
||||
p2PDiscoveryOptionGroup.maxPeers,
|
||||
p2PDiscoveryOptionGroup.minPeers,
|
||||
p2PDiscoveryOptionGroup.p2pHost,
|
||||
p2PDiscoveryOptionGroup.p2pInterface,
|
||||
p2PDiscoveryOptionGroup.p2pPort,
|
||||
@@ -1974,17 +1975,29 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
|
||||
}
|
||||
|
||||
private void ensureValidPeerBoundParams() {
|
||||
final int min = unstableNetworkingOptions.toDomainObject().getRlpx().getPeerLowerBound();
|
||||
final int max = p2PDiscoveryOptionGroup.maxPeers;
|
||||
if (min > max) {
|
||||
logger.warn("`--Xp2p-peer-lower-bound` " + min + " must not exceed --max-peers " + max);
|
||||
// modify the --X lower-bound value if it's not valid, we don't want unstable defaults
|
||||
// breaking things
|
||||
logger.warn("setting --Xp2p-peer-lower-bound=" + max);
|
||||
unstableNetworkingOptions.toDomainObject().getRlpx().setPeerLowerBound(max);
|
||||
p2PDiscoveryOptionGroup.minPeers = max;
|
||||
maxPeers = p2PDiscoveryOptionGroup.maxPeers;
|
||||
peersLowerBound = unstableNetworkingOptions.toDomainObject().getPeerLowerBound();
|
||||
if (peersLowerBound > maxPeers) {
|
||||
logger.warn(
|
||||
"`--Xp2p-peer-lower-bound` "
|
||||
+ peersLowerBound
|
||||
+ " must not exceed --max-peers "
|
||||
+ maxPeers);
|
||||
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 {
|
||||
p2PDiscoveryOptionGroup.minPeers = min;
|
||||
maxRemoteInitiatedPeers = maxPeers;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1996,7 +2009,8 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
|
||||
|| rpcEndpointServiceImpl.hasNamespace(apiName);
|
||||
|
||||
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);
|
||||
throw new ParameterException(
|
||||
this.commandLine,
|
||||
@@ -2005,12 +2019,12 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
|
||||
}
|
||||
|
||||
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);
|
||||
throw new ParameterException(
|
||||
this.commandLine,
|
||||
"Invalid value for option '--rpc-ws-api': invalid entries found "
|
||||
+ invalidWsApis.toString());
|
||||
"Invalid value for option '--rpc-ws-api': invalid entries found " + invalidWsApis);
|
||||
}
|
||||
|
||||
final boolean validHttpApiMethods =
|
||||
@@ -2212,8 +2226,8 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
|
||||
}
|
||||
}
|
||||
|
||||
private void initController() {
|
||||
besuController = buildController();
|
||||
private BesuController initController() {
|
||||
return buildController();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2241,6 +2255,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
|
||||
updateNetworkConfig(network), genesisConfigOverrides, getDefaultSyncModeIfNotSet())
|
||||
.synchronizerConfiguration(buildSyncConfig())
|
||||
.ethProtocolConfiguration(unstableEthProtocolOptions.toDomainObject())
|
||||
.networkConfiguration(unstableNetworkingOptions.toDomainObject())
|
||||
.dataDirectory(dataDir())
|
||||
.miningParameters(
|
||||
new MiningParameters.Builder()
|
||||
@@ -2284,6 +2299,9 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
|
||||
.evmConfiguration(unstableEvmOptions.toDomainObject())
|
||||
.dataStorageConfiguration(dataStorageOptions.toDomainObject())
|
||||
.maxPeers(p2PDiscoveryOptionGroup.maxPeers)
|
||||
.lowerBoundPeers(peersLowerBound)
|
||||
.maxRemotelyInitiatedPeers(maxRemoteInitiatedPeers)
|
||||
.randomPeerPriority(p2PDiscoveryOptionGroup.randomPeerPriority)
|
||||
.chainPruningConfiguration(unstableChainPruningOptions.toDomainObject());
|
||||
}
|
||||
|
||||
@@ -2944,8 +2962,6 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
|
||||
final Optional<TLSConfiguration> p2pTLSConfiguration,
|
||||
final boolean peerDiscoveryEnabled,
|
||||
final EthNetworkConfig ethNetworkConfig,
|
||||
final int maxPeers,
|
||||
final int minPeers,
|
||||
final String p2pAdvertisedHost,
|
||||
final String p2pListenInterface,
|
||||
final int p2pListenPort,
|
||||
@@ -2979,14 +2995,6 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
|
||||
.p2pAdvertisedHost(p2pAdvertisedHost)
|
||||
.p2pListenInterface(p2pListenInterface)
|
||||
.p2pListenPort(p2pListenPort)
|
||||
.maxPeers(maxPeers)
|
||||
.minPeers(minPeers)
|
||||
.limitRemoteWireConnectionsEnabled(
|
||||
p2PDiscoveryOptionGroup.isLimitRemoteWireConnectionsEnabled)
|
||||
.fractionRemoteConnectionsAllowed(
|
||||
Fraction.fromPercentage(p2PDiscoveryOptionGroup.maxRemoteConnectionsPercentage)
|
||||
.getValue())
|
||||
.randomPeerPriority(p2PDiscoveryOptionGroup.randomPeerPriority)
|
||||
.networkingConfiguration(unstableNetworkingOptions.toDomainObject())
|
||||
.legacyForkId(unstableEthProtocolOptions.toDomainObject().isLegacyEth64ForkIdEnabled())
|
||||
.graphQLConfiguration(graphQLConfiguration)
|
||||
|
||||
@@ -27,13 +27,13 @@ import picocli.CommandLine;
|
||||
|
||||
/** The Networking Cli options. */
|
||||
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 =
|
||||
"--Xp2p-initiate-connections-frequency";
|
||||
private final String CHECK_MAINTAINED_CONNECTIONS_FREQUENCY_FLAG =
|
||||
"--Xp2p-check-maintained-connections-frequency";
|
||||
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 P2P_PEER_LOWER_BOUND_FLAG = "--Xp2p-peer-lower-bound";
|
||||
/** The constant FILTER_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(
|
||||
hidden = true,
|
||||
names = {P2P_PEER_LOWER_BOUND_FLAG},
|
||||
names = PEER_LOWER_BOUND_FLAG,
|
||||
description =
|
||||
"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() {}
|
||||
|
||||
@@ -109,6 +109,7 @@ public class NetworkingOptions implements CLIOptions<NetworkingConfiguration> {
|
||||
cliOptions.initiateConnectionsFrequencySec =
|
||||
networkingConfig.getInitiateConnectionsFrequencySec();
|
||||
cliOptions.dnsDiscoveryServerOverride = networkingConfig.getDnsDiscoveryServerOverride();
|
||||
cliOptions.peerLowerBoundConfig = networkingConfig.getPeerLowerBound();
|
||||
|
||||
return cliOptions;
|
||||
}
|
||||
@@ -121,7 +122,7 @@ public class NetworkingOptions implements CLIOptions<NetworkingConfiguration> {
|
||||
config.setDnsDiscoveryServerOverride(dnsDiscoveryServerOverride);
|
||||
config.getDiscovery().setDiscoveryV5Enabled(isPeerDiscoveryV5Enabled);
|
||||
config.getDiscovery().setFilterOnEnrForkId(filterOnEnrForkId);
|
||||
config.getRlpx().setPeerLowerBound(peerLowerBound);
|
||||
config.setPeerLowerBound(peerLowerBoundConfig);
|
||||
return config;
|
||||
}
|
||||
|
||||
@@ -132,7 +133,9 @@ public class NetworkingOptions implements CLIOptions<NetworkingConfiguration> {
|
||||
CHECK_MAINTAINED_CONNECTIONS_FREQUENCY_FLAG,
|
||||
OptionParser.format(checkMaintainedConnectionsFrequencySec),
|
||||
INITIATE_CONNECTIONS_FREQUENCY_FLAG,
|
||||
OptionParser.format(initiateConnectionsFrequencySec));
|
||||
OptionParser.format(initiateConnectionsFrequencySec),
|
||||
PEER_LOWER_BOUND_FLAG,
|
||||
OptionParser.format((peerLowerBoundConfig)));
|
||||
|
||||
if (dnsDiscoveryServerOverride.isPresent()) {
|
||||
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.PrivacyParameters;
|
||||
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.sync.SyncMode;
|
||||
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 PluginServiceFactory additionalPluginServices;
|
||||
private final SyncState syncState;
|
||||
private final EthPeers ethPeers;
|
||||
|
||||
/**
|
||||
* Instantiates a new Besu controller.
|
||||
@@ -108,7 +110,8 @@ public class BesuController implements java.io.Closeable {
|
||||
final JsonRpcMethods additionalJsonRpcMethodsFactory,
|
||||
final NodeKey nodeKey,
|
||||
final List<Closeable> closeables,
|
||||
final PluginServiceFactory additionalPluginServices) {
|
||||
final PluginServiceFactory additionalPluginServices,
|
||||
final EthPeers ethPeers) {
|
||||
this.protocolSchedule = protocolSchedule;
|
||||
this.protocolContext = protocolContext;
|
||||
this.ethProtocolManager = ethProtocolManager;
|
||||
@@ -124,6 +127,7 @@ public class BesuController implements java.io.Closeable {
|
||||
this.closeables = closeables;
|
||||
this.miningParameters = miningParameters;
|
||||
this.additionalPluginServices = additionalPluginServices;
|
||||
this.ethPeers = ethPeers;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -207,6 +211,10 @@ public class BesuController implements java.io.Closeable {
|
||||
return miningCoordinator;
|
||||
}
|
||||
|
||||
public EthPeers getEthPeers() {
|
||||
return ethPeers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
closeables.forEach(this::tryClose);
|
||||
@@ -215,7 +223,7 @@ public class BesuController implements java.io.Closeable {
|
||||
private void tryClose(final Closeable closeable) {
|
||||
try {
|
||||
closeable.close();
|
||||
} catch (IOException e) {
|
||||
} catch (final IOException 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.mainnet.ProtocolSchedule;
|
||||
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.storage.StorageProvider;
|
||||
import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueSegmentIdentifier;
|
||||
@@ -169,9 +170,15 @@ public abstract class BesuControllerBuilder implements MiningParameterOverrides
|
||||
protected EvmConfiguration evmConfiguration;
|
||||
/** The Max peers. */
|
||||
protected int maxPeers;
|
||||
|
||||
private int peerLowerBound;
|
||||
private int maxRemotelyInitiatedPeers;
|
||||
/** The Chain pruner configuration. */
|
||||
protected ChainPrunerConfiguration chainPrunerConfiguration = ChainPrunerConfiguration.DEFAULT;
|
||||
|
||||
private NetworkingConfiguration networkingConfiguration;
|
||||
private Boolean randomPeerPriority;
|
||||
|
||||
/**
|
||||
* Storage provider besu controller builder.
|
||||
*
|
||||
@@ -443,6 +450,29 @@ public abstract class BesuControllerBuilder implements MiningParameterOverrides
|
||||
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.
|
||||
*
|
||||
@@ -455,6 +485,17 @@ public abstract class BesuControllerBuilder implements MiningParameterOverrides
|
||||
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.
|
||||
*
|
||||
@@ -475,6 +516,7 @@ public abstract class BesuControllerBuilder implements MiningParameterOverrides
|
||||
checkNotNull(storageProvider, "Must supply a storage provider");
|
||||
checkNotNull(gasLimitCalculator, "Missing gas limit calculator");
|
||||
checkNotNull(evmConfiguration, "Missing evm config");
|
||||
checkNotNull(networkingConfiguration, "Missing network configuration");
|
||||
prepForBuild();
|
||||
|
||||
final ProtocolSchedule protocolSchedule = createProtocolSchedule();
|
||||
@@ -557,9 +599,13 @@ public abstract class BesuControllerBuilder implements MiningParameterOverrides
|
||||
currentProtocolSpecSupplier,
|
||||
clock,
|
||||
metricsSystem,
|
||||
maxPeers,
|
||||
maxMessageSize,
|
||||
messagePermissioningProviders);
|
||||
messagePermissioningProviders,
|
||||
nodeKey.getPublicKey().getEncodedBytes(),
|
||||
peerLowerBound,
|
||||
maxPeers,
|
||||
maxRemotelyInitiatedPeers,
|
||||
randomPeerPriority);
|
||||
|
||||
final EthMessages ethMessages = new EthMessages();
|
||||
final EthMessages snapMessages = new EthMessages();
|
||||
@@ -679,7 +725,8 @@ public abstract class BesuControllerBuilder implements MiningParameterOverrides
|
||||
additionalJsonRpcMethodFactory,
|
||||
nodeKey,
|
||||
closeables,
|
||||
additionalPluginServices);
|
||||
additionalPluginServices,
|
||||
ethPeers);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -287,7 +287,7 @@ public class TransitionBesuControllerBuilder extends BesuControllerBuilder {
|
||||
|
||||
@Override
|
||||
public BesuController build() {
|
||||
BesuController controller = super.build();
|
||||
final BesuController controller = super.build();
|
||||
PostMergeContext.get().setSyncState(controller.getSyncState());
|
||||
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.transactions.TransactionPoolConfiguration;
|
||||
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.PrivateTransaction;
|
||||
import org.hyperledger.besu.ethereum.privacy.storage.PrivacyGroupHeadBlockMap;
|
||||
@@ -199,6 +200,7 @@ public class PrivacyReorgTest {
|
||||
.transactionPoolConfiguration(TransactionPoolConfiguration.DEFAULT)
|
||||
.gasLimitCalculator(GasLimitCalculator.constant())
|
||||
.evmConfiguration(EvmConfiguration.DEFAULT)
|
||||
.networkConfiguration(NetworkingConfiguration.create())
|
||||
.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.SynchronizerConfiguration;
|
||||
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.keyvalue.PrivacyKeyValueStorageProviderBuilder;
|
||||
import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueSegmentIdentifier;
|
||||
@@ -120,6 +121,7 @@ public class PrivacyTest {
|
||||
.transactionPoolConfiguration(TransactionPoolConfiguration.DEFAULT)
|
||||
.gasLimitCalculator(GasLimitCalculator.constant())
|
||||
.evmConfiguration(EvmConfiguration.DEFAULT)
|
||||
.networkConfiguration(NetworkingConfiguration.create())
|
||||
.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.Synchronizer;
|
||||
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.EthScheduler;
|
||||
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
|
||||
import org.hyperledger.besu.ethereum.mainnet.MainnetBlockHeaderFunctions;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
|
||||
import org.hyperledger.besu.ethereum.p2p.config.NetworkingConfiguration;
|
||||
import org.hyperledger.besu.ethereum.p2p.config.SubProtocolConfiguration;
|
||||
import org.hyperledger.besu.ethereum.p2p.peers.EnodeURLImpl;
|
||||
import org.hyperledger.besu.ethereum.storage.StorageProvider;
|
||||
@@ -138,6 +140,7 @@ public final class RunnerBuilderTest {
|
||||
when(besuController.getSynchronizer()).thenReturn(mock(Synchronizer.class));
|
||||
when(besuController.getMiningCoordinator()).thenReturn(mock(MiningCoordinator.class));
|
||||
when(besuController.getMiningCoordinator()).thenReturn(mock(MergeMiningCoordinator.class));
|
||||
when(besuController.getEthPeers()).thenReturn(mock(EthPeers.class));
|
||||
final GenesisConfigOptions genesisConfigOptions = mock(GenesisConfigOptions.class);
|
||||
when(genesisConfigOptions.getForkBlockNumbers()).thenReturn(Collections.emptyList());
|
||||
when(genesisConfigOptions.getForkBlockTimestamps()).thenReturn(Collections.emptyList());
|
||||
@@ -389,6 +392,7 @@ public final class RunnerBuilderTest {
|
||||
.storageProvider(mock(KeyValueStorageProvider.class))
|
||||
.rpcEndpointService(new RpcEndpointServiceImpl())
|
||||
.besuPluginContext(mock(BesuPluginContextImpl.class))
|
||||
.networkingConfiguration(NetworkingConfiguration.create())
|
||||
.build();
|
||||
|
||||
assertThat(runner.getJsonRpcPort()).isPresent();
|
||||
@@ -435,6 +439,7 @@ public final class RunnerBuilderTest {
|
||||
.storageProvider(mock(KeyValueStorageProvider.class))
|
||||
.rpcEndpointService(new RpcEndpointServiceImpl())
|
||||
.besuPluginContext(mock(BesuPluginContextImpl.class))
|
||||
.networkingConfiguration(NetworkingConfiguration.create())
|
||||
.build();
|
||||
|
||||
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.ProtocolSchedule;
|
||||
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.storage.StorageProvider;
|
||||
import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueSegmentIdentifier;
|
||||
@@ -188,7 +189,6 @@ public final class RunnerTest {
|
||||
.discovery(true)
|
||||
.p2pAdvertisedHost(listenHost)
|
||||
.p2pListenPort(0)
|
||||
.maxPeers(3)
|
||||
.metricsSystem(noOpMetricsSystem)
|
||||
.permissioningService(new PermissioningServiceImpl())
|
||||
.staticNodes(emptySet())
|
||||
@@ -459,6 +459,11 @@ public final class RunnerTest {
|
||||
.transactionPoolConfiguration(TransactionPoolConfiguration.DEFAULT)
|
||||
.gasLimitCalculator(GasLimitCalculator.constant())
|
||||
.evmConfiguration(EvmConfiguration.DEFAULT)
|
||||
.networkConfiguration(NetworkingConfiguration.create())
|
||||
.randomPeerPriority(Boolean.FALSE)
|
||||
.maxPeers(25)
|
||||
.lowerBoundPeers(25)
|
||||
.maxRemotelyInitiatedPeers(15)
|
||||
.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.mainnet.ProtocolSchedule;
|
||||
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.evm.internal.EvmConfiguration;
|
||||
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
|
||||
@@ -98,6 +99,7 @@ public final class RlpBlockExporterTest {
|
||||
.transactionPoolConfiguration(TransactionPoolConfiguration.DEFAULT)
|
||||
.gasLimitCalculator(GasLimitCalculator.constant())
|
||||
.evmConfiguration(EvmConfiguration.DEFAULT)
|
||||
.networkConfiguration(NetworkingConfiguration.create())
|
||||
.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.SynchronizerConfiguration;
|
||||
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.metrics.noop.NoOpMetricsSystem;
|
||||
import org.hyperledger.besu.testutil.TestClock;
|
||||
@@ -432,6 +433,7 @@ public abstract class JsonBlockImporterTest {
|
||||
.transactionPoolConfiguration(TransactionPoolConfiguration.DEFAULT)
|
||||
.gasLimitCalculator(GasLimitCalculator.constant())
|
||||
.evmConfiguration(EvmConfiguration.DEFAULT)
|
||||
.networkConfiguration(NetworkingConfiguration.create())
|
||||
.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.SynchronizerConfiguration;
|
||||
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.metrics.noop.NoOpMetricsSystem;
|
||||
import org.hyperledger.besu.testutil.BlockTestUtil;
|
||||
@@ -75,6 +76,7 @@ public final class RlpBlockImporterTest {
|
||||
.transactionPoolConfiguration(TransactionPoolConfiguration.DEFAULT)
|
||||
.gasLimitCalculator(GasLimitCalculator.constant())
|
||||
.evmConfiguration(EvmConfiguration.DEFAULT)
|
||||
.networkConfiguration(NetworkingConfiguration.create())
|
||||
.build();
|
||||
final RlpBlockImporter.ImportResult result =
|
||||
rlpBlockImporter.importBlockchain(source, targetController, false);
|
||||
@@ -107,6 +109,7 @@ public final class RlpBlockImporterTest {
|
||||
.transactionPoolConfiguration(TransactionPoolConfiguration.DEFAULT)
|
||||
.gasLimitCalculator(GasLimitCalculator.constant())
|
||||
.evmConfiguration(EvmConfiguration.DEFAULT)
|
||||
.networkConfiguration(NetworkingConfiguration.create())
|
||||
.build();
|
||||
|
||||
assertThatThrownBy(
|
||||
@@ -136,6 +139,7 @@ public final class RlpBlockImporterTest {
|
||||
.transactionPoolConfiguration(TransactionPoolConfiguration.DEFAULT)
|
||||
.gasLimitCalculator(GasLimitCalculator.constant())
|
||||
.evmConfiguration(EvmConfiguration.DEFAULT)
|
||||
.networkConfiguration(NetworkingConfiguration.create())
|
||||
.build();
|
||||
|
||||
final RlpBlockImporter.ImportResult result =
|
||||
|
||||
@@ -249,8 +249,6 @@ public class BesuCommandTest extends CommandTestAbstract {
|
||||
MAINNET_DISCOVERY_URL));
|
||||
verify(mockRunnerBuilder).p2pAdvertisedHost(eq("127.0.0.1"));
|
||||
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).graphQLConfiguration(eq(DEFAULT_GRAPH_QL_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).gasLimitCalculator(eq(GasLimitCalculator.constant()));
|
||||
verify(mockControllerBuilder).maxPeers(eq(maxPeers));
|
||||
verify(mockControllerBuilder).lowerBoundPeers(eq(maxPeers));
|
||||
verify(mockControllerBuilder).maxRemotelyInitiatedPeers(eq((int) Math.floor(0.6 * maxPeers)));
|
||||
verify(mockControllerBuilder).build();
|
||||
|
||||
assertThat(storageProviderArgumentCaptor.getValue()).isNotNull();
|
||||
@@ -401,7 +401,6 @@ public class BesuCommandTest extends CommandTestAbstract {
|
||||
verify(mockRunnerBuilder).ethNetworkConfig(ethNetworkConfigArgumentCaptor.capture());
|
||||
verify(mockRunnerBuilder).p2pAdvertisedHost(eq("1.2.3.4"));
|
||||
verify(mockRunnerBuilder).p2pListenPort(eq(1234));
|
||||
verify(mockRunnerBuilder).maxPeers(eq(42));
|
||||
verify(mockRunnerBuilder).jsonRpcConfiguration(eq(jsonRpcConfiguration));
|
||||
verify(mockRunnerBuilder).graphQLConfiguration(eq(graphQLConfiguration));
|
||||
verify(mockRunnerBuilder).webSocketConfiguration(eq(webSocketConfiguration));
|
||||
@@ -876,9 +875,6 @@ public class BesuCommandTest extends CommandTestAbstract {
|
||||
MAINNET_DISCOVERY_URL));
|
||||
verify(mockRunnerBuilder).p2pAdvertisedHost(eq("127.0.0.1"));
|
||||
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).graphQLConfiguration(eq(graphQLConfiguration));
|
||||
verify(mockRunnerBuilder).webSocketConfiguration(eq(webSocketConfiguration));
|
||||
@@ -1581,8 +1577,8 @@ public class BesuCommandTest extends CommandTestAbstract {
|
||||
final int maxPeers = 123;
|
||||
parseCommand("--max-peers", String.valueOf(maxPeers));
|
||||
|
||||
verify(mockRunnerBuilder).maxPeers(intArgumentCaptor.capture());
|
||||
verify(mockRunnerBuilder).build();
|
||||
verify(mockControllerBuilder).maxPeers(intArgumentCaptor.capture());
|
||||
verify(mockControllerBuilder).build();
|
||||
|
||||
assertThat(intArgumentCaptor.getValue()).isEqualTo(maxPeers);
|
||||
|
||||
@@ -1613,10 +1609,10 @@ public class BesuCommandTest extends CommandTestAbstract {
|
||||
assertThat(commandOutput.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);
|
||||
|
||||
verify(mockRunnerBuilder).minPeers(intArgumentCaptor.capture());
|
||||
verify(mockControllerBuilder).lowerBoundPeers(intArgumentCaptor.capture());
|
||||
assertThat(intArgumentCaptor.getValue()).isEqualTo(maxPeers);
|
||||
|
||||
verify(mockRunnerBuilder).build();
|
||||
@@ -1648,10 +1644,10 @@ public class BesuCommandTest extends CommandTestAbstract {
|
||||
"--Xp2p-peer-lower-bound",
|
||||
String.valueOf(minPeers));
|
||||
|
||||
verify(mockRunnerBuilder).maxPeers(intArgumentCaptor.capture());
|
||||
verify(mockControllerBuilder).maxPeers(intArgumentCaptor.capture());
|
||||
assertThat(intArgumentCaptor.getValue()).isEqualTo(maxPeers);
|
||||
|
||||
verify(mockRunnerBuilder).minPeers(intArgumentCaptor.capture());
|
||||
verify(mockControllerBuilder).lowerBoundPeers(intArgumentCaptor.capture());
|
||||
assertThat(intArgumentCaptor.getValue()).isEqualTo(minPeers);
|
||||
|
||||
verify(mockRunnerBuilder).build();
|
||||
@@ -1665,16 +1661,16 @@ public class BesuCommandTest extends CommandTestAbstract {
|
||||
|
||||
final int remoteConnectionsPercentage = 12;
|
||||
parseCommand(
|
||||
"--remote-connections-limit-enabled",
|
||||
"--remote-connections-limit-enabled=true",
|
||||
"--remote-connections-max-percentage",
|
||||
String.valueOf(remoteConnectionsPercentage));
|
||||
|
||||
verify(mockRunnerBuilder).fractionRemoteConnectionsAllowed(floatCaptor.capture());
|
||||
verify(mockRunnerBuilder).build();
|
||||
verify(mockControllerBuilder).maxRemotelyInitiatedPeers(intArgumentCaptor.capture());
|
||||
verify(mockControllerBuilder).build();
|
||||
|
||||
assertThat(floatCaptor.getValue())
|
||||
assertThat(intArgumentCaptor.getValue())
|
||||
.isEqualTo(
|
||||
Fraction.fromPercentage(Percentage.fromInt(remoteConnectionsPercentage)).getValue());
|
||||
(int) Math.floor(25 * Fraction.fromPercentage(remoteConnectionsPercentage).getValue()));
|
||||
|
||||
assertThat(commandOutput.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");
|
||||
}
|
||||
|
||||
@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
|
||||
public void syncMode_fast() {
|
||||
parseCommand("--sync-mode", "FAST");
|
||||
@@ -1915,7 +1895,7 @@ public class BesuCommandTest extends CommandTestAbstract {
|
||||
|
||||
@Test
|
||||
public void launcherDefaultOptionValue() {
|
||||
TestBesuCommand besuCommand = parseCommand();
|
||||
final TestBesuCommand besuCommand = parseCommand();
|
||||
|
||||
assertThat(besuCommand.getLauncherOptions().isLauncherMode()).isFalse();
|
||||
assertThat(besuCommand.getEnodeDnsConfiguration().updateEnabled()).isFalse();
|
||||
@@ -3379,7 +3359,7 @@ public class BesuCommandTest extends CommandTestAbstract {
|
||||
|
||||
@Test
|
||||
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();
|
||||
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");
|
||||
}
|
||||
|
||||
@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
|
||||
public void kzgTrustedSetupFileRequiresDataBlobEnabledNetwork() throws IOException {
|
||||
final Path genesisFileWithoutBlobs =
|
||||
|
||||
@@ -17,7 +17,6 @@ package org.hyperledger.besu.cli;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||
import static org.mockito.ArgumentMatchers.anyFloat;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.anyLong;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
@@ -228,8 +227,14 @@ public abstract class CommandTestAbstract {
|
||||
when(mockControllerBuilder.reorgLoggingThreshold(anyLong())).thenReturn(mockControllerBuilder);
|
||||
when(mockControllerBuilder.dataStorageConfiguration(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.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(mockController).when(mockControllerBuilder).build();
|
||||
lenient().when(mockController.getProtocolManager()).thenReturn(mockEthProtocolManager);
|
||||
@@ -255,13 +260,6 @@ public abstract class CommandTestAbstract {
|
||||
when(mockRunnerBuilder.p2pListenPort(anyInt())).thenReturn(mockRunnerBuilder);
|
||||
when(mockRunnerBuilder.p2pListenInterface(anyString())).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.natMethod(any())).thenReturn(mockRunnerBuilder);
|
||||
when(mockRunnerBuilder.natManagerServiceName(any())).thenReturn(mockRunnerBuilder);
|
||||
@@ -379,7 +377,7 @@ public abstract class CommandTestAbstract {
|
||||
// reset GlobalOpenTelemetry
|
||||
GlobalOpenTelemetry.resetForTest();
|
||||
|
||||
TestBesuCommand besuCommand = getTestBesuCommand(testType);
|
||||
final TestBesuCommand besuCommand = getTestBesuCommand(testType);
|
||||
besuCommands.add(besuCommand);
|
||||
|
||||
final File defaultKeyFile =
|
||||
|
||||
@@ -16,7 +16,6 @@ package org.hyperledger.besu.cli.options;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
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.ethereum.p2p.config.NetworkingConfiguration;
|
||||
@@ -129,32 +128,6 @@ public class NetworkingOptionsTest
|
||||
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
|
||||
public void checkFilterByForkIdNotSet() {
|
||||
final TestBesuCommand cmd = parseCommand();
|
||||
@@ -203,6 +176,7 @@ public class NetworkingOptionsTest
|
||||
NetworkingConfiguration.DEFAULT_INITIATE_CONNECTIONS_FREQUENCY_SEC + 10);
|
||||
config.setCheckMaintainedConnectionsFrequency(
|
||||
NetworkingConfiguration.DEFAULT_CHECK_MAINTAINED_CONNECTIONS_FREQUENCY_SEC + 10);
|
||||
config.setPeerLowerBound(NetworkingConfiguration.DEFAULT_PEER_LOWER_BOUND - 10);
|
||||
return config;
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ import org.hyperledger.besu.config.GenesisConfigFile;
|
||||
import org.hyperledger.besu.config.GenesisConfigOptions;
|
||||
import org.hyperledger.besu.config.Keccak256ConfigOptions;
|
||||
import org.hyperledger.besu.cryptoservices.NodeKey;
|
||||
import org.hyperledger.besu.cryptoservices.NodeKeyUtils;
|
||||
import org.hyperledger.besu.datatypes.Hash;
|
||||
import org.hyperledger.besu.ethereum.GasLimitCalculator;
|
||||
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.transactions.TransactionPoolConfiguration;
|
||||
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.keyvalue.KeyValueSegmentIdentifier;
|
||||
import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueStoragePrefixedKeyBlockchainStorage;
|
||||
@@ -74,6 +76,7 @@ import org.mockito.junit.MockitoJUnitRunner;
|
||||
public class BesuControllerBuilderTest {
|
||||
|
||||
private BesuControllerBuilder besuControllerBuilder;
|
||||
private static final NodeKey nodeKey = NodeKeyUtils.generate();
|
||||
|
||||
@Mock GenesisConfigFile genesisConfigFile;
|
||||
@Mock GenesisConfigOptions genesisConfigOptions;
|
||||
@@ -87,7 +90,6 @@ public class BesuControllerBuilderTest {
|
||||
@Mock PrivacyParameters privacyParameters;
|
||||
@Mock Clock clock;
|
||||
@Mock TransactionPoolConfiguration poolConfiguration;
|
||||
@Mock NodeKey nodeKey;
|
||||
@Mock StorageProvider storageProvider;
|
||||
@Mock GasLimitCalculator gasLimitCalculator;
|
||||
@Mock WorldStateStorage worldStateStorage;
|
||||
@@ -157,6 +159,7 @@ public class BesuControllerBuilderTest {
|
||||
.nodeKey(nodeKey)
|
||||
.storageProvider(storageProvider)
|
||||
.evmConfiguration(EvmConfiguration.DEFAULT)
|
||||
.networkConfiguration(NetworkingConfiguration.create())
|
||||
.networkId(networkId);
|
||||
}
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ import org.hyperledger.besu.config.GenesisConfigFile;
|
||||
import org.hyperledger.besu.config.GenesisConfigOptions;
|
||||
import org.hyperledger.besu.consensus.merge.MergeContext;
|
||||
import org.hyperledger.besu.cryptoservices.NodeKey;
|
||||
import org.hyperledger.besu.cryptoservices.NodeKeyUtils;
|
||||
import org.hyperledger.besu.datatypes.Hash;
|
||||
import org.hyperledger.besu.datatypes.Wei;
|
||||
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.feemarket.BaseFeeMarket;
|
||||
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.keyvalue.KeyValueStoragePrefixedKeyBlockchainStorage;
|
||||
import org.hyperledger.besu.ethereum.worldstate.DataStorageFormat;
|
||||
@@ -79,6 +81,7 @@ import org.mockito.junit.MockitoJUnitRunner;
|
||||
public class MergeBesuControllerBuilderTest {
|
||||
|
||||
private MergeBesuControllerBuilder besuControllerBuilder;
|
||||
private static final NodeKey nodeKey = NodeKeyUtils.generate();
|
||||
|
||||
@Mock GenesisConfigFile genesisConfigFile;
|
||||
@Mock GenesisConfigOptions genesisConfigOptions;
|
||||
@@ -90,7 +93,6 @@ public class MergeBesuControllerBuilderTest {
|
||||
@Mock PrivacyParameters privacyParameters;
|
||||
@Mock Clock clock;
|
||||
@Mock TransactionPoolConfiguration poolConfiguration;
|
||||
@Mock NodeKey nodeKey;
|
||||
@Mock StorageProvider storageProvider;
|
||||
@Mock GasLimitCalculator gasLimitCalculator;
|
||||
@Mock WorldStateStorage worldStateStorage;
|
||||
@@ -161,6 +163,7 @@ public class MergeBesuControllerBuilderTest {
|
||||
.nodeKey(nodeKey)
|
||||
.storageProvider(storageProvider)
|
||||
.evmConfiguration(EvmConfiguration.DEFAULT)
|
||||
.networkConfiguration(NetworkingConfiguration.create())
|
||||
.networkId(networkId);
|
||||
}
|
||||
|
||||
@@ -169,7 +172,7 @@ public class MergeBesuControllerBuilderTest {
|
||||
when(genesisConfigOptions.getTerminalTotalDifficulty())
|
||||
.thenReturn(Optional.of(UInt256.valueOf(1500L)));
|
||||
|
||||
Difficulty terminalTotalDifficulty =
|
||||
final Difficulty terminalTotalDifficulty =
|
||||
visitWithMockConfigs(new MergeBesuControllerBuilder())
|
||||
.build()
|
||||
.getProtocolContext()
|
||||
@@ -181,9 +184,9 @@ public class MergeBesuControllerBuilderTest {
|
||||
|
||||
@Test
|
||||
public void assertConfiguredBlock() {
|
||||
Blockchain mockChain = mock(Blockchain.class);
|
||||
final Blockchain mockChain = mock(Blockchain.class);
|
||||
when(mockChain.getBlockHeader(anyLong())).thenReturn(Optional.of(mock(BlockHeader.class)));
|
||||
MergeContext mergeContext =
|
||||
final MergeContext mergeContext =
|
||||
besuControllerBuilder.createConsensusContext(
|
||||
mockChain,
|
||||
mock(WorldStateArchive.class),
|
||||
@@ -205,10 +208,10 @@ public class MergeBesuControllerBuilderTest {
|
||||
mock(WorldStateArchive.class),
|
||||
this.besuControllerBuilder.createProtocolSchedule()));
|
||||
assertThat(mergeContext).isNotNull();
|
||||
Difficulty over = Difficulty.of(10000L);
|
||||
Difficulty under = Difficulty.of(10L);
|
||||
final Difficulty over = Difficulty.of(10000L);
|
||||
final Difficulty under = Difficulty.of(10L);
|
||||
|
||||
BlockHeader parent =
|
||||
final BlockHeader parent =
|
||||
headerGenerator
|
||||
.difficulty(under)
|
||||
.parentHash(genesisState.getBlock().getHash())
|
||||
@@ -218,7 +221,7 @@ public class MergeBesuControllerBuilderTest {
|
||||
.buildHeader();
|
||||
blockchain.appendBlock(new Block(parent, BlockBody.empty()), Collections.emptyList());
|
||||
|
||||
BlockHeader terminal =
|
||||
final BlockHeader terminal =
|
||||
headerGenerator
|
||||
.difficulty(over)
|
||||
.parentHash(parent.getHash())
|
||||
@@ -233,9 +236,9 @@ public class MergeBesuControllerBuilderTest {
|
||||
|
||||
@Test
|
||||
public void assertNoFinalizedBlockWhenNotStored() {
|
||||
Blockchain mockChain = mock(Blockchain.class);
|
||||
final Blockchain mockChain = mock(Blockchain.class);
|
||||
when(mockChain.getFinalized()).thenReturn(Optional.empty());
|
||||
MergeContext mergeContext =
|
||||
final MergeContext mergeContext =
|
||||
besuControllerBuilder.createConsensusContext(
|
||||
mockChain,
|
||||
mock(WorldStateArchive.class),
|
||||
@@ -252,7 +255,7 @@ public class MergeBesuControllerBuilderTest {
|
||||
when(mockChain.getFinalized()).thenReturn(Optional.of(finalizedHeader.getHash()));
|
||||
when(mockChain.getBlockHeader(finalizedHeader.getHash()))
|
||||
.thenReturn(Optional.of(finalizedHeader));
|
||||
MergeContext mergeContext =
|
||||
final MergeContext mergeContext =
|
||||
besuControllerBuilder.createConsensusContext(
|
||||
mockChain,
|
||||
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.transactions.TransactionPoolConfiguration;
|
||||
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.keyvalue.KeyValueStoragePrefixedKeyBlockchainStorage;
|
||||
import org.hyperledger.besu.ethereum.worldstate.DataStorageFormat;
|
||||
@@ -144,7 +145,8 @@ public class QbftBesuControllerBuilderTest {
|
||||
.nodeKey(nodeKey)
|
||||
.storageProvider(storageProvider)
|
||||
.gasLimitCalculator(gasLimitCalculator)
|
||||
.evmConfiguration(EvmConfiguration.DEFAULT);
|
||||
.evmConfiguration(EvmConfiguration.DEFAULT)
|
||||
.networkConfiguration(NetworkingConfiguration.create());
|
||||
}
|
||||
|
||||
@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.datatypes.Address;
|
||||
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.wire.Capability;
|
||||
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.Message;
|
||||
@@ -107,6 +108,11 @@ public class BftProtocolManager implements ProtocolManager {
|
||||
peers.add(peerConnection);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldConnect(final Peer peer, final boolean incoming) {
|
||||
return false; // for now the EthProtocolManager takes care of this
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleDisconnect(
|
||||
final PeerConnection peerConnection,
|
||||
|
||||
@@ -42,7 +42,7 @@ public class EthSynchronizerUpdaterTest {
|
||||
|
||||
@Test
|
||||
public void ethPeerIsMissingResultInNoUpdate() {
|
||||
when(ethPeers.peer(any())).thenReturn(null);
|
||||
when(ethPeers.peer(any(PeerConnection.class))).thenReturn(null);
|
||||
|
||||
final EthSynchronizerUpdater updater = new EthSynchronizerUpdater(ethPeers);
|
||||
|
||||
@@ -53,7 +53,7 @@ public class EthSynchronizerUpdaterTest {
|
||||
|
||||
@Test
|
||||
public void chainStateUpdateIsAttemptedIfEthPeerExists() {
|
||||
when(ethPeers.peer(any())).thenReturn(ethPeer);
|
||||
when(ethPeers.peer(any(PeerConnection.class))).thenReturn(ethPeer);
|
||||
when(ethPeer.chainState()).thenReturn(chainState);
|
||||
|
||||
final EthSynchronizerUpdater updater = new EthSynchronizerUpdater(ethPeers);
|
||||
|
||||
@@ -91,11 +91,11 @@ public class JsonRpcExecutor {
|
||||
|
||||
return rpcProcessor.process(
|
||||
id, method, span, new JsonRpcRequestContext(requestBody, optionalUser, alive));
|
||||
} catch (IllegalArgumentException e) {
|
||||
} catch (final IllegalArgumentException e) {
|
||||
try {
|
||||
final Integer id = jsonRpcRequest.getInteger("id", null);
|
||||
return new JsonRpcErrorResponse(id, INVALID_REQUEST);
|
||||
} catch (ClassCastException idNotIntegerException) {
|
||||
} catch (final ClassCastException idNotIntegerException) {
|
||||
return new JsonRpcErrorResponse(null, INVALID_REQUEST);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,7 +79,8 @@ public class AdminJsonRpcHttpServiceTest extends JsonRpcHttpServiceTestBase {
|
||||
List.of(),
|
||||
EthProtocolConfiguration.DEFAULT_MAX_MESSAGE_SIZE,
|
||||
TestClock.fixed(),
|
||||
Collections.emptyList()));
|
||||
Collections.emptyList(),
|
||||
Bytes.random(64)));
|
||||
peerList.add(
|
||||
new EthPeer(
|
||||
MockPeerConnection.create(info2, addr30301, addr60302),
|
||||
@@ -88,7 +89,8 @@ public class AdminJsonRpcHttpServiceTest extends JsonRpcHttpServiceTestBase {
|
||||
List.of(),
|
||||
EthProtocolConfiguration.DEFAULT_MAX_MESSAGE_SIZE,
|
||||
TestClock.fixed(),
|
||||
Collections.emptyList()));
|
||||
Collections.emptyList(),
|
||||
Bytes.random(64)));
|
||||
peerList.add(
|
||||
new EthPeer(
|
||||
MockPeerConnection.create(info3, addr30301, addr60303),
|
||||
@@ -97,7 +99,8 @@ public class AdminJsonRpcHttpServiceTest extends JsonRpcHttpServiceTestBase {
|
||||
List.of(),
|
||||
EthProtocolConfiguration.DEFAULT_MAX_MESSAGE_SIZE,
|
||||
TestClock.fixed(),
|
||||
Collections.emptyList()));
|
||||
Collections.emptyList(),
|
||||
Bytes.random(64)));
|
||||
|
||||
when(ethPeersMock.streamAllPeers()).thenReturn(peerList.stream());
|
||||
when(peerDiscoveryMock.getPeerCount()).thenReturn(peerList.size());
|
||||
|
||||
@@ -64,6 +64,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import io.vertx.core.Vertx;
|
||||
@@ -280,6 +281,8 @@ public class JsonRpcHttpServiceRpcApisTest {
|
||||
.blockchain(blockchain)
|
||||
.blockNumberForks(Collections.emptyList())
|
||||
.timestampForks(Collections.emptyList())
|
||||
.allConnectionsSupplier(Stream::empty)
|
||||
.allActiveConnectionsSupplier(Stream::empty)
|
||||
.build();
|
||||
|
||||
p2pNetwork.start();
|
||||
|
||||
@@ -17,12 +17,16 @@ package org.hyperledger.besu.ethereum.api.jsonrpc;
|
||||
import static org.mockito.Mockito.mock;
|
||||
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.Peer;
|
||||
import org.hyperledger.besu.ethereum.p2p.rlpx.connections.PeerConnection;
|
||||
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.PeerInfo;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
|
||||
public class MockPeerConnection {
|
||||
PeerInfo peerInfo;
|
||||
InetSocketAddress localAddress;
|
||||
@@ -32,8 +36,11 @@ public class MockPeerConnection {
|
||||
final PeerInfo peerInfo,
|
||||
final InetSocketAddress localAddress,
|
||||
final InetSocketAddress remoteAddress) {
|
||||
PeerConnection peerConnection = mock(PeerConnection.class);
|
||||
final PeerConnection peerConnection = mock(PeerConnection.class);
|
||||
when(peerConnection.getPeerInfo()).thenReturn(peerInfo);
|
||||
if (peerInfo != null) {
|
||||
when(peerConnection.getPeer()).thenReturn(createPeer(peerInfo.getNodeId()));
|
||||
}
|
||||
when(peerConnection.getLocalAddress()).thenReturn(localAddress);
|
||||
when(peerConnection.getRemoteAddress()).thenReturn(remoteAddress);
|
||||
when(peerConnection.getRemoteEnode())
|
||||
@@ -47,4 +54,12 @@ public class MockPeerConnection {
|
||||
|
||||
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(),
|
||||
EthProtocolConfiguration.DEFAULT_MAX_MESSAGE_SIZE,
|
||||
TestClock.fixed(),
|
||||
Collections.emptyList());
|
||||
Collections.emptyList(),
|
||||
Bytes.random(64));
|
||||
return Lists.newArrayList(ethPeer);
|
||||
}
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ public class EthPeer implements Comparable<EthPeer> {
|
||||
|
||||
private static final int MAX_OUTSTANDING_REQUESTS = 5;
|
||||
|
||||
private final PeerConnection connection;
|
||||
private PeerConnection connection;
|
||||
|
||||
private final int maxTrackedSeenBlocks = 300;
|
||||
|
||||
@@ -81,6 +81,7 @@ public class EthPeer implements Comparable<EthPeer> {
|
||||
return size() > maxTrackedSeenBlocks;
|
||||
}
|
||||
}));
|
||||
private final Bytes localNodeId;
|
||||
|
||||
private Optional<BlockHeader> checkpointHeader = Optional.empty();
|
||||
|
||||
@@ -89,7 +90,7 @@ public class EthPeer implements Comparable<EthPeer> {
|
||||
private final Clock clock;
|
||||
private final List<NodeMessagePermissioningProvider> permissioningProviders;
|
||||
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 fullyValidated = new AtomicBoolean(false);
|
||||
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 PeerReputation reputation = new PeerReputation();
|
||||
private final Map<PeerValidator, Boolean> validationStatus = new ConcurrentHashMap<>();
|
||||
private final Bytes id;
|
||||
|
||||
private static final Map<Integer, Integer> roundMessages;
|
||||
|
||||
@@ -126,7 +128,8 @@ public class EthPeer implements Comparable<EthPeer> {
|
||||
final List<PeerValidator> peerValidators,
|
||||
final int maxMessageSize,
|
||||
final Clock clock,
|
||||
final List<NodeMessagePermissioningProvider> permissioningProviders) {
|
||||
final List<NodeMessagePermissioningProvider> permissioningProviders,
|
||||
final Bytes localNodeId) {
|
||||
this.connection = connection;
|
||||
this.protocolName = protocolName;
|
||||
this.maxMessageSize = maxMessageSize;
|
||||
@@ -137,6 +140,8 @@ public class EthPeer implements Comparable<EthPeer> {
|
||||
fullyValidated.set(peerValidators.isEmpty());
|
||||
|
||||
this.requestManagers = new ConcurrentHashMap<>();
|
||||
this.localNodeId = localNodeId;
|
||||
this.id = connection.getPeer().getId();
|
||||
|
||||
initEthRequestManagers();
|
||||
initSnapRequestManagers();
|
||||
@@ -228,13 +233,32 @@ public class EthPeer implements Comparable<EthPeer> {
|
||||
|
||||
public RequestManager.ResponseStream send(
|
||||
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))) {
|
||||
LOG.debug("Protocol {} unavailable for this peer {}", protocolName, this);
|
||||
return null;
|
||||
}
|
||||
if (permissioningProviders.stream()
|
||||
.anyMatch(p -> !p.isMessagePermitted(connection.getRemoteEnode(), messageData.getCode()))) {
|
||||
.anyMatch(
|
||||
p -> !p.isMessagePermitted(connectionToUse.getRemoteEnode(), messageData.getCode()))) {
|
||||
LOG.info(
|
||||
"Permissioning blocked sending of message code {} to {}", messageData.getCode(), this);
|
||||
if (LOG.isDebugEnabled()) {
|
||||
@@ -243,7 +267,8 @@ public class EthPeer implements Comparable<EthPeer> {
|
||||
permissioningProviders.stream()
|
||||
.filter(
|
||||
p ->
|
||||
!p.isMessagePermitted(connection.getRemoteEnode(), messageData.getCode())));
|
||||
!p.isMessagePermitted(
|
||||
connectionToUse.getRemoteEnode(), messageData.getCode())));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -266,7 +291,7 @@ public class EthPeer implements Comparable<EthPeer> {
|
||||
}
|
||||
}
|
||||
|
||||
connection.sendForProtocol(protocolName, messageData);
|
||||
connectionToUse.sendForProtocol(protocolName, messageData);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -433,27 +458,51 @@ public class EthPeer implements Comparable<EthPeer> {
|
||||
knownBlocks.add(hash);
|
||||
}
|
||||
|
||||
public void registerStatusSent() {
|
||||
statusHasBeenSentToPeer.set(true);
|
||||
maybeExecuteStatusesExchangedCallback();
|
||||
public void registerStatusSent(final PeerConnection connection) {
|
||||
synchronized (this) {
|
||||
connection.setStatusSent();
|
||||
maybeExecuteStatusesExchangedCallback(connection);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
lastProtocolVersion.set(protocolVersion);
|
||||
statusHasBeenReceivedFromPeer.set(true);
|
||||
maybeExecuteStatusesExchangedCallback();
|
||||
synchronized (this) {
|
||||
connection.setStatusReceived();
|
||||
maybeExecuteStatusesExchangedCallback(connection);
|
||||
}
|
||||
}
|
||||
|
||||
private void maybeExecuteStatusesExchangedCallback() {
|
||||
if (readyForRequests()) {
|
||||
final Consumer<EthPeer> callback = onStatusesExchanged.getAndSet(null);
|
||||
if (callback == null) {
|
||||
return;
|
||||
private void maybeExecuteStatusesExchangedCallback(final PeerConnection newConnection) {
|
||||
synchronized (this) {
|
||||
if (newConnection.getStatusExchanged()) {
|
||||
if (!this.connection.equals(newConnection)) {
|
||||
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.
|
||||
*/
|
||||
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 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) {
|
||||
return knownBlocks.contains(hash);
|
||||
}
|
||||
@@ -559,7 +599,7 @@ public class EthPeer implements Comparable<EthPeer> {
|
||||
isFullyValidated(),
|
||||
isDisconnected(),
|
||||
connection.getPeerInfo().getClientId(),
|
||||
System.identityHashCode(connection),
|
||||
connection,
|
||||
connection.getPeer().getEnodeURLString());
|
||||
}
|
||||
|
||||
@@ -590,6 +630,41 @@ public class EthPeer implements Comparable<EthPeer> {
|
||||
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
|
||||
public interface DisconnectCallback {
|
||||
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.peervalidation.PeerValidator;
|
||||
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.wire.messages.DisconnectMessage;
|
||||
import org.hyperledger.besu.metrics.BesuMetricCategory;
|
||||
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.util.Subscribers;
|
||||
|
||||
import java.time.Clock;
|
||||
import java.time.Duration;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
@@ -34,12 +37,18 @@ import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
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.LoggerFactory;
|
||||
|
||||
@@ -57,52 +66,64 @@ public class EthPeers {
|
||||
public static final Comparator<EthPeer> LEAST_TO_MOST_BUSY =
|
||||
Comparator.comparing(EthPeer::outstandingRequests)
|
||||
.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 Clock clock;
|
||||
private final List<NodeMessagePermissioningProvider> permissioningProviders;
|
||||
private final int maxPeers;
|
||||
private final int maxMessageSize;
|
||||
private final Subscribers<ConnectCallback> connectCallbacks = Subscribers.create();
|
||||
private final Subscribers<DisconnectCallback> disconnectCallbacks = Subscribers.create();
|
||||
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 Comparator<EthPeer> bestPeerComparator;
|
||||
private final Bytes localNodeId;
|
||||
private RlpxAgent rlpxAgent;
|
||||
|
||||
private final Counter connectedPeersCounter;
|
||||
|
||||
public EthPeers(
|
||||
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(
|
||||
final String protocolName,
|
||||
final Supplier<ProtocolSpec> currentProtocolSpecSupplier,
|
||||
final Clock clock,
|
||||
final MetricsSystem metricsSystem,
|
||||
final int maxPeers,
|
||||
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.currentProtocolSpecSupplier = currentProtocolSpecSupplier;
|
||||
this.clock = clock;
|
||||
this.permissioningProviders = permissioningProviders;
|
||||
this.maxPeers = maxPeers;
|
||||
this.maxMessageSize = maxMessageSize;
|
||||
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(
|
||||
BesuMetricCategory.ETHEREUM,
|
||||
"peer_count",
|
||||
@@ -113,39 +134,83 @@ public class EthPeers {
|
||||
"pending_peer_requests_current",
|
||||
"Number of peer requests currently pending because peers are busy",
|
||||
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 registerConnection(
|
||||
final PeerConnection peerConnection, final List<PeerValidator> peerValidators) {
|
||||
final EthPeer peer =
|
||||
new EthPeer(
|
||||
peerConnection,
|
||||
protocolName,
|
||||
this::invokeConnectionCallbacks,
|
||||
peerValidators,
|
||||
maxMessageSize,
|
||||
clock,
|
||||
permissioningProviders);
|
||||
connections.putIfAbsent(peerConnection, peer);
|
||||
LOG.debug("Adding new EthPeer {}", peer.nodeId());
|
||||
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 registerDisconnect(final PeerConnection connection) {
|
||||
final EthPeer peer = connections.remove(connection);
|
||||
if (peer != null) {
|
||||
disconnectCallbacks.forEach(callback -> callback.onDisconnect(peer));
|
||||
peer.handleDisconnect();
|
||||
abortPendingRequestsAssignedToDisconnectedPeers();
|
||||
LOG.debug("Disconnected EthPeer {}", peer);
|
||||
public int getPeerLowerBound() {
|
||||
return peerLowerBound;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private List<PeerConnection> getIncompleteConnections(final Bytes id) {
|
||||
return incompleteConnections.asMap().keySet().stream()
|
||||
.filter(nrc -> nrc.getPeer().getId().equals(id))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public boolean registerDisconnect(final PeerConnection connection) {
|
||||
final Bytes id = connection.getPeer().getId();
|
||||
final EthPeer peer = completeConnections.get(id);
|
||||
return registerDisconnect(id, peer, connection);
|
||||
}
|
||||
|
||||
private boolean registerDisconnect(
|
||||
final Bytes id, final EthPeer peer, final PeerConnection connection) {
|
||||
incompleteConnections.invalidate(connection);
|
||||
boolean removed = false;
|
||||
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();
|
||||
return removed;
|
||||
}
|
||||
|
||||
private boolean peerHasIncompleteConnection(final Bytes id) {
|
||||
return getIncompleteConnections(id).stream().anyMatch(conn -> !conn.isDisconnected());
|
||||
}
|
||||
|
||||
private void abortPendingRequestsAssignedToDisconnectedPeers() {
|
||||
synchronized (this) {
|
||||
final Iterator<PendingPeerRequest> iterator = pendingRequests.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
final PendingPeerRequest request = iterator.next();
|
||||
for (final PendingPeerRequest request : pendingRequests) {
|
||||
if (request.getAssignedPeer().map(EthPeer::isDisconnected).orElse(false)) {
|
||||
request.abort();
|
||||
}
|
||||
@@ -153,8 +218,17 @@ public class EthPeers {
|
||||
}
|
||||
}
|
||||
|
||||
public EthPeer peer(final PeerConnection peerConnection) {
|
||||
return connections.get(peerConnection);
|
||||
public EthPeer peer(final PeerConnection connection) {
|
||||
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(
|
||||
@@ -179,7 +253,7 @@ public class EthPeers {
|
||||
|
||||
public void dispatchMessage(
|
||||
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()) {
|
||||
reattemptPendingPeerRequests();
|
||||
}
|
||||
@@ -216,28 +290,30 @@ public class EthPeers {
|
||||
}
|
||||
|
||||
public int peerCount() {
|
||||
return connections.size();
|
||||
removeDisconnectedPeers();
|
||||
return completeConnections.size();
|
||||
}
|
||||
|
||||
public int getMaxPeers() {
|
||||
return maxPeers;
|
||||
return peerUpperBound;
|
||||
}
|
||||
|
||||
public Stream<EthPeer> streamAllPeers() {
|
||||
return connections.values().stream();
|
||||
return completeConnections.values().stream();
|
||||
}
|
||||
|
||||
private void removeDisconnectedPeers() {
|
||||
final Collection<EthPeer> peerStream = connections.values();
|
||||
for (EthPeer p : peerStream) {
|
||||
if (p.isDisconnected()) {
|
||||
connections.remove(p.getConnection());
|
||||
}
|
||||
}
|
||||
completeConnections
|
||||
.values()
|
||||
.forEach(
|
||||
ep -> {
|
||||
if (ep.isDisconnected()) {
|
||||
registerDisconnect(ep.getId(), ep, ep.getConnection());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public Stream<EthPeer> streamAvailablePeers() {
|
||||
removeDisconnectedPeers();
|
||||
return streamAllPeers()
|
||||
.filter(EthPeer::readyForRequests)
|
||||
.filter(peer -> !peer.isDisconnected());
|
||||
@@ -269,6 +345,43 @@ public class EthPeers {
|
||||
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() {
|
||||
streamAvailablePeers()
|
||||
.sorted(getBestChainComparator())
|
||||
@@ -293,18 +406,173 @@ public class EthPeers {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (connections.isEmpty()) {
|
||||
if (completeConnections.isEmpty()) {
|
||||
return "0 EthPeers {}";
|
||||
}
|
||||
final String connectionsList =
|
||||
connections.values().stream()
|
||||
completeConnections.values().stream()
|
||||
.sorted()
|
||||
.map(EthPeer::toString)
|
||||
.collect(Collectors.joining(", \n"));
|
||||
return connections.size() + " EthPeers {\n" + connectionsList + '}';
|
||||
return completeConnections.size() + " EthPeers {\n" + connectionsList + '}';
|
||||
}
|
||||
|
||||
private void invokeConnectionCallbacks(final EthPeer peer) {
|
||||
connectCallbacks.forEach(cb -> cb.onPeerConnected(peer));
|
||||
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 int compareByMaskedNodeId(final PeerConnection a, final PeerConnection b) {
|
||||
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.ForkIdManager;
|
||||
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.PeerNotConnected;
|
||||
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.Capability;
|
||||
@@ -283,7 +284,7 @@ public class EthProtocolManager implements ProtocolManager, MinedBlockObserver {
|
||||
|
||||
// Handle STATUS processing
|
||||
if (code == EthPV62.STATUS) {
|
||||
handleStatusMessage(ethPeer, messageData);
|
||||
handleStatusMessage(ethPeer, message);
|
||||
return;
|
||||
} else if (!ethPeer.statusHasBeenReceived()) {
|
||||
// Peers are required to send status messages before any other message type
|
||||
@@ -352,17 +353,12 @@ public class EthProtocolManager implements ProtocolManager, MinedBlockObserver {
|
||||
|
||||
@Override
|
||||
public void handleNewConnection(final PeerConnection connection) {
|
||||
ethPeers.registerConnection(connection, peerValidators);
|
||||
ethPeers.registerNewConnection(connection, peerValidators);
|
||||
final EthPeer peer = ethPeers.peer(connection);
|
||||
if (peer.statusHasBeenSentToPeer()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Capability cap = connection.capability(getSupportedProtocol());
|
||||
final ForkId latestForkId =
|
||||
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 =
|
||||
StatusMessage.create(
|
||||
cap.getVersion(),
|
||||
@@ -372,42 +368,58 @@ public class EthProtocolManager implements ProtocolManager, MinedBlockObserver {
|
||||
genesisHash,
|
||||
latestForkId);
|
||||
try {
|
||||
LOG.debug("Sending status message to {}.", peer);
|
||||
peer.send(status, getSupportedProtocol());
|
||||
peer.registerStatusSent();
|
||||
LOG.trace("Sending status message to {} for connection {}.", peer.getId(), connection);
|
||||
peer.send(status, getSupportedProtocol(), connection);
|
||||
peer.registerStatusSent(connection);
|
||||
} catch (final PeerNotConnected peerNotConnected) {
|
||||
// Nothing to do.
|
||||
}
|
||||
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
|
||||
public void handleDisconnect(
|
||||
final PeerConnection connection,
|
||||
final DisconnectReason reason,
|
||||
final boolean initiatedByPeer) {
|
||||
ethPeers.registerDisconnect(connection);
|
||||
LOG.debug(
|
||||
"Disconnect - {} - {} - {} - {} peers left",
|
||||
initiatedByPeer ? "Inbound" : "Outbound",
|
||||
reason,
|
||||
connection.getPeerInfo(),
|
||||
ethPeers.peerCount());
|
||||
LOG.trace("{}", ethPeers);
|
||||
if (ethPeers.registerDisconnect(connection)) {
|
||||
LOG.debug(
|
||||
"Disconnect - {} - {} - {} - {} peers left\n{}",
|
||||
initiatedByPeer ? "Inbound" : "Outbound",
|
||||
reason,
|
||||
connection.getPeer().getId(),
|
||||
ethPeers.peerCount(),
|
||||
ethPeers);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleStatusMessage(final EthPeer peer, final MessageData data) {
|
||||
final StatusMessage status = StatusMessage.readFrom(data);
|
||||
private void handleStatusMessage(final EthPeer peer, final Message message) {
|
||||
final StatusMessage status = StatusMessage.readFrom(message.getData());
|
||||
final ForkId forkId = status.forkId();
|
||||
peer.getConnection().getPeer().setForkId(forkId);
|
||||
try {
|
||||
if (!status.networkId().equals(networkId)) {
|
||||
LOG.debug("Mismatched network id: {}, EthPeer {}", status.networkId(), peer);
|
||||
peer.disconnect(DisconnectReason.SUBPROTOCOL_TRIGGERED);
|
||||
} else if (!forkIdManager.peerCheck(status.forkId()) && status.protocolVersion() > 63) {
|
||||
} else if (!forkIdManager.peerCheck(forkId) && status.protocolVersion() > 63) {
|
||||
LOG.debug(
|
||||
"{} has matching network id ({}), but non-matching fork id: {}",
|
||||
peer,
|
||||
networkId,
|
||||
status.forkId());
|
||||
forkId);
|
||||
peer.disconnect(DisconnectReason.SUBPROTOCOL_TRIGGERED);
|
||||
} else if (forkIdManager.peerCheck(status.genesisHash())) {
|
||||
LOG.debug(
|
||||
@@ -421,9 +433,16 @@ public class EthProtocolManager implements ProtocolManager, MinedBlockObserver {
|
||||
LOG.debug("Post-merge disconnect: peer still PoW {}", peer);
|
||||
handleDisconnect(peer.getConnection(), DisconnectReason.SUBPROTOCOL_TRIGGERED, false);
|
||||
} else {
|
||||
LOG.debug("Received status message from {}: {}", peer, status);
|
||||
LOG.debug(
|
||||
"Received status message from {}: {} with connection {}",
|
||||
peer,
|
||||
status,
|
||||
message.getConnection());
|
||||
peer.registerStatusReceived(
|
||||
status.bestHash(), status.totalDifficulty(), status.protocolVersion());
|
||||
status.bestHash(),
|
||||
status.totalDifficulty(),
|
||||
status.protocolVersion(),
|
||||
message.getConnection());
|
||||
}
|
||||
} catch (final RLPException 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.peervalidation.PeerValidator;
|
||||
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.wire.AbstractSnapMessageData;
|
||||
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.Capability;
|
||||
@@ -137,6 +138,11 @@ public class SnapProtocolManager implements ProtocolManager {
|
||||
@Override
|
||||
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
|
||||
public void handleDisconnect(
|
||||
final PeerConnection connection,
|
||||
|
||||
@@ -65,7 +65,7 @@ public class TrailingPeerLimiter implements BlockAddedObserver {
|
||||
while (!trailingPeers.isEmpty() && trailingPeers.size() > maxTrailingPeers) {
|
||||
final EthPeer peerToDisconnect = trailingPeers.remove(0);
|
||||
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 PeerInfo peerInfo;
|
||||
private Optional<DisconnectReason> disconnectReason = Optional.empty();
|
||||
private boolean statusSent;
|
||||
private boolean statusReceived;
|
||||
|
||||
public MockPeerConnection(final Set<Capability> caps, final PeerSendHandler onSend) {
|
||||
this.caps = caps;
|
||||
@@ -111,11 +113,36 @@ public class MockPeerConnection implements PeerConnection {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getInitiatedAt() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean inboundInitiated() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDisconnected() {
|
||||
return disconnected;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStatusSent() {
|
||||
this.statusSent = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStatusReceived() {
|
||||
this.statusReceived = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getStatusExchanged() {
|
||||
return statusSent && statusReceived;
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface PeerSendHandler {
|
||||
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.singletonList;
|
||||
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.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
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 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.messages.BlockBodiesMessage;
|
||||
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.p2p.rlpx.connections.PeerConnection;
|
||||
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.PeerInfo;
|
||||
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.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -55,7 +57,10 @@ import org.junit.Test;
|
||||
public class EthPeerTest {
|
||||
private static final BlockDataGenerator gen = new BlockDataGenerator();
|
||||
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
|
||||
public void getHeadersStream() throws PeerNotConnected {
|
||||
@@ -340,10 +345,10 @@ public class EthPeerTest {
|
||||
when(trueProvider.isMessagePermitted(any(), anyInt())).thenReturn(true);
|
||||
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());
|
||||
|
||||
verify(peer.getConnection(), times(0)).sendForProtocol(any(), eq(PingMessage.get()));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -357,13 +362,13 @@ public class EthPeerTest {
|
||||
@Test
|
||||
public void compareTo_withDifferentNodeId() {
|
||||
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(peer2.compareTo(peer1)).isEqualTo(-1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void recordUsefullResponse() {
|
||||
public void recordUsefulResponse() {
|
||||
final EthPeer peer = createPeer();
|
||||
peer.recordUselessResponse("bodies");
|
||||
final EthPeer peer2 = createPeer();
|
||||
@@ -463,11 +468,13 @@ public class EthPeerTest {
|
||||
|
||||
private EthPeer createPeerWithPeerInfo(final Bytes nodeId) {
|
||||
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
|
||||
// that extend the sub-protocol work correctly
|
||||
PeerInfo peerInfo = new PeerInfo(1, "clientId", Collections.emptyList(), 30303, nodeId);
|
||||
when(peerConnection.getPeerInfo()).thenReturn(peerInfo);
|
||||
when(peerConnection.getPeer()).thenReturn(EthPeerTestUtil.createPeer(peerInfo.getNodeId()));
|
||||
|
||||
final Consumer<EthPeer> onPeerReady = (peer) -> {};
|
||||
return new EthPeer(
|
||||
peerConnection,
|
||||
"foo",
|
||||
@@ -475,13 +482,32 @@ public class EthPeerTest {
|
||||
Collections.emptyList(),
|
||||
EthProtocolConfiguration.DEFAULT_MAX_MESSAGE_SIZE,
|
||||
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(
|
||||
final List<PeerValidator> peerValidators,
|
||||
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) -> {};
|
||||
// Use a non-eth protocol name to ensure that EthPeer with sub-protocols such as Istanbul
|
||||
// that extend the sub-protocol work correctly
|
||||
@@ -492,7 +518,17 @@ public class EthPeerTest {
|
||||
peerValidators,
|
||||
EthProtocolConfiguration.DEFAULT_MAX_MESSAGE_SIZE,
|
||||
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
|
||||
|
||||
@@ -337,22 +337,6 @@ public class EthPeersTest {
|
||||
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
|
||||
public void toString_hasExpectedInfo() {
|
||||
assertThat(ethPeers.toString()).isEqualTo("0 EthPeers {}");
|
||||
@@ -360,7 +344,7 @@ public class EthPeersTest {
|
||||
final EthPeer peerA =
|
||||
EthProtocolManagerTestUtil.createPeer(ethProtocolManager, Difficulty.of(50), 20)
|
||||
.getEthPeer();
|
||||
ethPeers.registerConnection(peerA.getConnection(), Collections.emptyList());
|
||||
ethPeers.registerNewConnection(peerA.getConnection(), Collections.emptyList());
|
||||
assertThat(ethPeers.toString()).contains("1 EthPeers {");
|
||||
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.EthProtocolConfiguration;
|
||||
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.sync.SynchronizerConfiguration;
|
||||
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
|
||||
@@ -45,6 +46,8 @@ import java.util.Collections;
|
||||
import java.util.Optional;
|
||||
import java.util.OptionalLong;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
|
||||
public class EthProtocolManagerTestUtil {
|
||||
|
||||
public static EthProtocolManager create(
|
||||
@@ -71,17 +74,22 @@ public class EthProtocolManagerTestUtil {
|
||||
final EthProtocolConfiguration ethereumWireProtocolConfiguration,
|
||||
final Optional<MergePeerFilter> mergePeerFilter) {
|
||||
|
||||
EthPeers peers =
|
||||
final EthPeers peers =
|
||||
new EthPeers(
|
||||
EthProtocol.NAME,
|
||||
() -> protocolSchedule.getByBlockHeader(blockchain.getChainHeadHeader()),
|
||||
TestClock.fixed(),
|
||||
new NoOpMetricsSystem(),
|
||||
EthProtocolConfiguration.DEFAULT_MAX_MESSAGE_SIZE,
|
||||
Collections.emptyList(),
|
||||
Bytes.random(64),
|
||||
25,
|
||||
EthProtocolConfiguration.DEFAULT_MAX_MESSAGE_SIZE);
|
||||
EthMessages messages = new EthMessages();
|
||||
EthScheduler ethScheduler = new DeterministicEthScheduler(TimeoutPolicy.NEVER_TIMEOUT);
|
||||
EthContext ethContext = new EthContext(peers, messages, ethScheduler);
|
||||
25,
|
||||
25,
|
||||
false);
|
||||
final EthMessages messages = new EthMessages();
|
||||
final EthScheduler ethScheduler = new DeterministicEthScheduler(TimeoutPolicy.NEVER_TIMEOUT);
|
||||
final EthContext ethContext = new EthContext(peers, messages, ethScheduler);
|
||||
|
||||
return new EthProtocolManager(
|
||||
blockchain,
|
||||
@@ -185,15 +193,21 @@ public class EthProtocolManagerTestUtil {
|
||||
final WorldStateArchive worldStateArchive,
|
||||
final TransactionPool transactionPool,
|
||||
final EthProtocolConfiguration configuration) {
|
||||
EthPeers peers =
|
||||
|
||||
final EthPeers peers =
|
||||
new EthPeers(
|
||||
EthProtocol.NAME,
|
||||
() -> protocolSchedule.getByBlockHeader(blockchain.getChainHeadHeader()),
|
||||
TestClock.fixed(),
|
||||
new NoOpMetricsSystem(),
|
||||
EthProtocolConfiguration.DEFAULT_MAX_MESSAGE_SIZE,
|
||||
Collections.emptyList(),
|
||||
Bytes.random(64),
|
||||
25,
|
||||
EthProtocolConfiguration.DEFAULT_MAX_MESSAGE_SIZE);
|
||||
EthMessages messages = new EthMessages();
|
||||
25,
|
||||
25,
|
||||
false);
|
||||
final EthMessages messages = new EthMessages();
|
||||
|
||||
return create(
|
||||
blockchain,
|
||||
@@ -214,15 +228,21 @@ public class EthProtocolManagerTestUtil {
|
||||
final TransactionPool transactionPool,
|
||||
final EthProtocolConfiguration configuration,
|
||||
final ForkIdManager forkIdManager) {
|
||||
EthPeers peers =
|
||||
|
||||
final EthPeers peers =
|
||||
new EthPeers(
|
||||
EthProtocol.NAME,
|
||||
() -> protocolSchedule.getByBlockHeader(blockchain.getChainHeadHeader()),
|
||||
TestClock.fixed(),
|
||||
new NoOpMetricsSystem(),
|
||||
EthProtocolConfiguration.DEFAULT_MAX_MESSAGE_SIZE,
|
||||
Collections.emptyList(),
|
||||
Bytes.random(64),
|
||||
25,
|
||||
EthProtocolConfiguration.DEFAULT_MAX_MESSAGE_SIZE);
|
||||
EthMessages messages = new EthMessages();
|
||||
25,
|
||||
25,
|
||||
false);
|
||||
final EthMessages messages = new EthMessages();
|
||||
|
||||
return create(
|
||||
blockchain,
|
||||
@@ -240,15 +260,20 @@ public class EthProtocolManagerTestUtil {
|
||||
final ProtocolSchedule protocolSchedule,
|
||||
final Blockchain blockchain,
|
||||
final EthScheduler ethScheduler) {
|
||||
EthPeers peers =
|
||||
final EthPeers peers =
|
||||
new EthPeers(
|
||||
EthProtocol.NAME,
|
||||
() -> protocolSchedule.getByBlockHeader(blockchain.getChainHeadHeader()),
|
||||
TestClock.fixed(),
|
||||
new NoOpMetricsSystem(),
|
||||
EthProtocolConfiguration.DEFAULT_MAX_MESSAGE_SIZE,
|
||||
Collections.emptyList(),
|
||||
Bytes.random(64),
|
||||
25,
|
||||
EthProtocolConfiguration.DEFAULT_MAX_MESSAGE_SIZE);
|
||||
EthMessages messages = new EthMessages();
|
||||
25,
|
||||
25,
|
||||
false);
|
||||
final EthMessages messages = new EthMessages();
|
||||
|
||||
return create(
|
||||
blockchain,
|
||||
@@ -392,6 +417,17 @@ public class EthProtocolManagerTestUtil {
|
||||
.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(
|
||||
final EthProtocolManager ethProtocolManager,
|
||||
final long estimatedHeight,
|
||||
|
||||
@@ -308,7 +308,8 @@ public class RequestManagerTest {
|
||||
Collections.emptyList(),
|
||||
EthProtocolConfiguration.DEFAULT_MAX_MESSAGE_SIZE,
|
||||
TestClock.fixed(),
|
||||
Collections.emptyList());
|
||||
Collections.emptyList(),
|
||||
Bytes.random(64));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -129,11 +129,11 @@ public class RespondingEthPeer {
|
||||
final MockPeerConnection peerConnection =
|
||||
new MockPeerConnection(
|
||||
caps, (cap, msg, conn) -> outgoingMessages.add(new OutgoingMessage(cap, msg)));
|
||||
ethPeers.registerConnection(peerConnection, peerValidators);
|
||||
ethPeers.registerNewConnection(peerConnection, peerValidators);
|
||||
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));
|
||||
peer.registerStatusSent();
|
||||
peer.registerStatusSent(peerConnection);
|
||||
|
||||
return new RespondingEthPeer(
|
||||
ethProtocolManager, snapProtocolManager, peerConnection, peer, outgoingMessages);
|
||||
|
||||
@@ -45,11 +45,13 @@ import org.hyperledger.besu.plugin.services.MetricsSystem;
|
||||
import org.hyperledger.besu.testutil.TestClock;
|
||||
|
||||
import java.time.ZoneId;
|
||||
import java.util.Collections;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
@@ -93,8 +95,14 @@ public abstract class AbstractMessageTaskTest<T, R> {
|
||||
() -> protocolSchedule.getByBlockHeader(blockchain.getChainHeadHeader()),
|
||||
TestClock.fixed(),
|
||||
metricsSystem,
|
||||
EthProtocolConfiguration.DEFAULT_MAX_MESSAGE_SIZE,
|
||||
Collections.emptyList(),
|
||||
Bytes.random(64),
|
||||
MAX_PEERS,
|
||||
EthProtocolConfiguration.DEFAULT_MAX_MESSAGE_SIZE));
|
||||
MAX_PEERS,
|
||||
MAX_PEERS,
|
||||
false));
|
||||
|
||||
final EthMessages ethMessages = new EthMessages();
|
||||
final EthScheduler ethScheduler =
|
||||
new DeterministicEthScheduler(
|
||||
|
||||
@@ -37,6 +37,7 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
@@ -166,6 +167,7 @@ public abstract class PeerMessageTaskTest<T>
|
||||
Collections.emptyList(),
|
||||
EthProtocolConfiguration.DEFAULT_MAX_MESSAGE_SIZE,
|
||||
TestClock.fixed(),
|
||||
Collections.emptyList());
|
||||
Collections.emptyList(),
|
||||
Bytes.random(64));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -623,8 +623,13 @@ public abstract class AbstractBlockPropagationManagerTest {
|
||||
() -> protocolSchedule.getByBlockHeader(blockchain.getChainHeadHeader()),
|
||||
TestClock.fixed(),
|
||||
metricsSystem,
|
||||
EthProtocolConfiguration.DEFAULT_MAX_MESSAGE_SIZE,
|
||||
Collections.emptyList(),
|
||||
Bytes.random(64),
|
||||
25,
|
||||
EthProtocolConfiguration.DEFAULT_MAX_MESSAGE_SIZE),
|
||||
25,
|
||||
25,
|
||||
false),
|
||||
new EthMessages(),
|
||||
ethScheduler);
|
||||
final BlockPropagationManager blockPropagationManager =
|
||||
@@ -749,6 +754,7 @@ public abstract class AbstractBlockPropagationManagerTest {
|
||||
return invocation.getArgument(0, Supplier.class).get();
|
||||
}
|
||||
});
|
||||
|
||||
final EthContext ethContext =
|
||||
new EthContext(
|
||||
new EthPeers(
|
||||
@@ -756,8 +762,13 @@ public abstract class AbstractBlockPropagationManagerTest {
|
||||
() -> protocolSchedule.getByBlockHeader(blockchain.getChainHeadHeader()),
|
||||
TestClock.fixed(),
|
||||
metricsSystem,
|
||||
EthProtocolConfiguration.DEFAULT_MAX_MESSAGE_SIZE,
|
||||
Collections.emptyList(),
|
||||
Bytes.random(64),
|
||||
25,
|
||||
EthProtocolConfiguration.DEFAULT_MAX_MESSAGE_SIZE),
|
||||
25,
|
||||
25,
|
||||
false),
|
||||
new EthMessages(),
|
||||
ethScheduler);
|
||||
final BlockPropagationManager blockPropagationManager =
|
||||
|
||||
@@ -145,7 +145,7 @@ public class TrailingPeerLimiterTest {
|
||||
final List<EthPeer> disconnected = asList(disconnectedPeers);
|
||||
for (final EthPeer peer : peers) {
|
||||
if (disconnected.contains(peer)) {
|
||||
verify(peer).disconnect(DisconnectReason.TOO_MANY_PEERS);
|
||||
verify(peer).disconnect(DisconnectReason.USELESS_PEER);
|
||||
} else {
|
||||
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.peers.DefaultPeer;
|
||||
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.wire.messages.DisconnectMessage.DisconnectReason;
|
||||
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
|
||||
import org.hyperledger.besu.evm.internal.EvmConfiguration;
|
||||
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.permissioning.NodeMessagePermissioningProvider;
|
||||
import org.hyperledger.besu.testutil.TestClock;
|
||||
|
||||
import java.io.Closeable;
|
||||
@@ -128,15 +131,26 @@ public class TestNode implements Closeable {
|
||||
when(syncState.isInitialSyncPhaseDone()).thenReturn(true);
|
||||
|
||||
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 =
|
||||
new EthPeers(
|
||||
EthProtocol.NAME,
|
||||
() -> protocolSchedule.getByBlockHeader(blockchain.getChainHeadHeader()),
|
||||
TestClock.fixed(),
|
||||
metricsSystem,
|
||||
EthProtocolConfiguration.DEFAULT_MAX_MESSAGE_SIZE,
|
||||
Collections.singletonList(nmpp),
|
||||
Bytes.random(64),
|
||||
25,
|
||||
EthProtocolConfiguration.DEFAULT_MAX_MESSAGE_SIZE);
|
||||
25,
|
||||
25,
|
||||
false);
|
||||
|
||||
final EthScheduler scheduler = new EthScheduler(1, 1, 1, metricsSystem);
|
||||
final EthContext ethContext = new EthContext(ethPeers, ethMessages, scheduler);
|
||||
@@ -183,10 +197,15 @@ public class TestNode implements Closeable {
|
||||
.blockchain(blockchain)
|
||||
.blockNumberForks(Collections.emptyList())
|
||||
.timestampForks(Collections.emptyList())
|
||||
.allConnectionsSupplier(ethPeers::getAllConnections)
|
||||
.allActiveConnectionsSupplier(ethPeers::getAllActiveConnections)
|
||||
.build())
|
||||
.metricsSystem(new NoOpMetricsSystem())
|
||||
.build();
|
||||
network = networkRunner.getNetwork();
|
||||
final RlpxAgent rlpxAgent = network.getRlpxAgent();
|
||||
rlpxAgent.subscribeConnectRequest((p, d) -> true);
|
||||
ethPeers.setRlpxAgent(rlpxAgent);
|
||||
network.subscribeDisconnect(
|
||||
(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.evm.internal.EvmConfiguration;
|
||||
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 java.math.BigInteger;
|
||||
@@ -58,6 +60,7 @@ import java.util.Collections;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
import org.assertj.core.api.Condition;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -95,14 +98,27 @@ public class TransactionPoolFactoryTest {
|
||||
public void setup() {
|
||||
when(blockchain.getBlockHashByNumber(anyLong())).thenReturn(Optional.of(mock(Hash.class)));
|
||||
when(context.getBlockchain()).thenReturn(blockchain);
|
||||
|
||||
final NodeMessagePermissioningProvider nmpp =
|
||||
new NodeMessagePermissioningProvider() {
|
||||
@Override
|
||||
public boolean isMessagePermitted(final EnodeURL destinationEnode, final int code) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
ethPeers =
|
||||
new EthPeers(
|
||||
"ETH",
|
||||
() -> protocolSpec,
|
||||
TestClock.fixed(),
|
||||
new NoOpMetricsSystem(),
|
||||
EthProtocolConfiguration.DEFAULT_MAX_MESSAGE_SIZE,
|
||||
Collections.singletonList(nmpp),
|
||||
Bytes.random(64),
|
||||
25,
|
||||
EthProtocolConfiguration.DEFAULT_MAX_MESSAGE_SIZE);
|
||||
25,
|
||||
25,
|
||||
false);
|
||||
when(ethContext.getEthMessages()).thenReturn(ethMessages);
|
||||
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.MessageData;
|
||||
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.plugin.data.EnodeURL;
|
||||
import org.hyperledger.besu.util.Subscribers;
|
||||
@@ -168,6 +169,9 @@ public final class MockNetwork {
|
||||
connectCallbacks.subscribe(callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void subscribeConnectRequest(final ShouldConnectCallback callback) {}
|
||||
|
||||
@Override
|
||||
public void subscribeDisconnect(final DisconnectCallback callback) {
|
||||
disconnectCallbacks.subscribe(callback);
|
||||
@@ -237,6 +241,8 @@ public final class MockNetwork {
|
||||
private final Peer to;
|
||||
|
||||
private final MockNetwork network;
|
||||
private boolean statusSent;
|
||||
private boolean statusReceived;
|
||||
|
||||
MockPeerConnection(final Peer source, final Peer target, final MockNetwork network) {
|
||||
from = source;
|
||||
@@ -308,5 +314,30 @@ public final class MockNetwork {
|
||||
public InetSocketAddress getRemoteAddress() {
|
||||
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());
|
||||
|
||||
if (invalidEnodes.size() > 0) {
|
||||
String invalidBootnodes =
|
||||
final String invalidBootnodes =
|
||||
invalidEnodes.stream().map(EnodeURL::toString).collect(Collectors.joining(","));
|
||||
String errorMsg =
|
||||
final String errorMsg =
|
||||
"Bootnodes must have discovery enabled. Invalid bootnodes: " + invalidBootnodes + ".";
|
||||
throw new IllegalArgumentException(errorMsg);
|
||||
}
|
||||
|
||||
@@ -22,12 +22,14 @@ import java.util.Optional;
|
||||
public class NetworkingConfiguration {
|
||||
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_PEER_LOWER_BOUND = 25;
|
||||
|
||||
private DiscoveryConfiguration discovery = new DiscoveryConfiguration();
|
||||
private RlpxConfiguration rlpx = new RlpxConfiguration();
|
||||
private int initiateConnectionsFrequencySec = DEFAULT_INITIATE_CONNECTIONS_FREQUENCY_SEC;
|
||||
private int checkMaintainedConnectionsFrequencySec =
|
||||
DEFAULT_CHECK_MAINTAINED_CONNECTIONS_FREQUENCY_SEC;
|
||||
private Integer peerLowerBound = DEFAULT_PEER_LOWER_BOUND;
|
||||
private Optional<String> dnsDiscoveryServerOverride = Optional.empty();
|
||||
|
||||
public static NetworkingConfiguration create() {
|
||||
@@ -84,6 +86,16 @@ public class NetworkingConfiguration {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Integer getPeerLowerBound() {
|
||||
return peerLowerBound;
|
||||
}
|
||||
|
||||
public NetworkingConfiguration setPeerLowerBound(final Integer peerLowerBoundConfig) {
|
||||
checkArgument(peerLowerBoundConfig > 0);
|
||||
this.peerLowerBound = peerLowerBoundConfig;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (o == this) {
|
||||
|
||||
@@ -14,8 +14,6 @@
|
||||
*/
|
||||
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.util.NetworkUtility;
|
||||
|
||||
@@ -29,10 +27,6 @@ public class RlpxConfiguration {
|
||||
private String clientId = "TestClient/1.0.0";
|
||||
private String bindHost = NetworkUtility.INADDR_ANY;
|
||||
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();
|
||||
|
||||
public static RlpxConfiguration create() {
|
||||
@@ -71,15 +65,6 @@ public class RlpxConfiguration {
|
||||
return this;
|
||||
}
|
||||
|
||||
public RlpxConfiguration setPeerUpperBound(final int peers) {
|
||||
peerUpperBound = peers;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getPeerUpperBound() {
|
||||
return peerUpperBound;
|
||||
}
|
||||
|
||||
public String getClientId() {
|
||||
return clientId;
|
||||
}
|
||||
@@ -89,29 +74,6 @@ public class RlpxConfiguration {
|
||||
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
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) {
|
||||
@@ -137,13 +99,4 @@ public class RlpxConfiguration {
|
||||
sb.append('}');
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<NodeRecord> getNodeRecord() {
|
||||
return Optional.ofNullable(nodeRecord);
|
||||
}
|
||||
@@ -141,10 +142,16 @@ public class DiscoveryPeer extends DefaultPeer {
|
||||
this.forkId = ForkId.fromRawForkId(nodeRecord.get("eth"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<ForkId> getForkId() {
|
||||
return this.forkId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setForkId(final ForkId forkId) {
|
||||
this.forkId = Optional.ofNullable(forkId);
|
||||
}
|
||||
|
||||
public boolean discoveryEndpointMatches(final DiscoveryPeer peer) {
|
||||
return peer.getEndpoint().getHost().equals(endpoint.getHost())
|
||||
&& 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.netty.TLSConfiguration;
|
||||
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.storage.StorageProvider;
|
||||
import org.hyperledger.besu.nat.NatMethod;
|
||||
@@ -68,6 +69,7 @@ import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@@ -183,7 +185,7 @@ public class DefaultP2PNetwork implements P2PNetwork {
|
||||
this.peerPermissions = peerPermissions;
|
||||
|
||||
// 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);
|
||||
peerDiscoveryAgent.addPeerRequirement(() -> rlpxAgent.getConnectionCount() >= peerLowerBound);
|
||||
subscribeDisconnect(reputationManager);
|
||||
@@ -302,6 +304,11 @@ public class DefaultP2PNetwork implements P2PNetwork {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public RlpxAgent getRlpxAgent() {
|
||||
return rlpxAgent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addMaintainedConnectionPeer(final Peer peer) {
|
||||
if (localNode.isReady()
|
||||
@@ -356,11 +363,15 @@ public class DefaultP2PNetwork implements P2PNetwork {
|
||||
if (!localNode.isReady()) {
|
||||
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
|
||||
.streamPeers()
|
||||
.filter(peer -> !peer.getEnodeURL().getNodeId().equals(localEnodeURL.getNodeId()))
|
||||
.filter(p -> !rlpxAgent.getPeerConnection(p).isPresent())
|
||||
.filter(p -> !doNotConnectTo.contains(p.getId()))
|
||||
.forEach(rlpxAgent::connect);
|
||||
}
|
||||
|
||||
@@ -379,6 +390,11 @@ public class DefaultP2PNetwork implements P2PNetwork {
|
||||
return rlpxAgent.streamConnections().collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPeerCount() {
|
||||
return getRlpxAgent().getConnectionCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<DiscoveryPeer> streamDiscoveredPeers() {
|
||||
return peerDiscoveryAgent.streamDiscoveredPeers();
|
||||
@@ -399,6 +415,12 @@ public class DefaultP2PNetwork implements P2PNetwork {
|
||||
rlpxAgent.subscribeConnect(callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void subscribeConnectRequest(final ShouldConnectCallback callback) {
|
||||
rlpxAgent.subscribeConnectRequest(callback);
|
||||
}
|
||||
;
|
||||
|
||||
@Override
|
||||
public void subscribeDisconnect(final DisconnectCallback callback) {
|
||||
rlpxAgent.subscribeDisconnect(callback);
|
||||
@@ -474,8 +496,6 @@ public class DefaultP2PNetwork implements P2PNetwork {
|
||||
private PeerPermissions peerPermissions = PeerPermissions.noop();
|
||||
|
||||
private NatService natService = new NatService(Optional.empty());
|
||||
private boolean randomPeerPriority;
|
||||
|
||||
private MetricsSystem metricsSystem;
|
||||
private StorageProvider storageProvider;
|
||||
private Optional<TLSConfiguration> p2pTLSConfiguration = Optional.empty();
|
||||
@@ -483,6 +503,9 @@ public class DefaultP2PNetwork implements P2PNetwork {
|
||||
private List<Long> blockNumberForks;
|
||||
private List<Long> timestampForks;
|
||||
private boolean legacyForkIdEnabled = false;
|
||||
private Supplier<Stream<PeerConnection>> allConnectionsSupplier;
|
||||
private Supplier<Stream<PeerConnection>> allActiveConnectionsSupplier;
|
||||
private int peersLowerBound;
|
||||
|
||||
public P2PNetwork build() {
|
||||
validate();
|
||||
@@ -526,6 +549,8 @@ public class DefaultP2PNetwork implements P2PNetwork {
|
||||
checkState(peerDiscoveryAgent != null || vertx != null, "Vertx must be set.");
|
||||
checkState(blockNumberForks != null, "BlockNumberForks 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() {
|
||||
@@ -546,6 +571,7 @@ public class DefaultP2PNetwork implements P2PNetwork {
|
||||
|
||||
private RlpxAgent createRlpxAgent(
|
||||
final LocalNode localNode, final PeerPrivileges peerPrivileges) {
|
||||
|
||||
return RlpxAgent.builder()
|
||||
.nodeKey(nodeKey)
|
||||
.config(config.getRlpx())
|
||||
@@ -553,8 +579,10 @@ public class DefaultP2PNetwork implements P2PNetwork {
|
||||
.peerPrivileges(peerPrivileges)
|
||||
.localNode(localNode)
|
||||
.metricsSystem(metricsSystem)
|
||||
.randomPeerPriority(randomPeerPriority)
|
||||
.p2pTLSConfiguration(p2pTLSConfiguration)
|
||||
.allConnectionsSupplier(allConnectionsSupplier)
|
||||
.allActiveConnectionsSupplier(allActiveConnectionsSupplier)
|
||||
.peersLowerBound(peersLowerBound)
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -570,11 +598,6 @@ public class DefaultP2PNetwork implements P2PNetwork {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder randomPeerPriority(final boolean randomPeerPriority) {
|
||||
this.randomPeerPriority = randomPeerPriority;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder vertx(final Vertx vertx) {
|
||||
checkNotNull(vertx);
|
||||
this.vertx = vertx;
|
||||
@@ -662,5 +685,22 @@ public class DefaultP2PNetwork implements P2PNetwork {
|
||||
this.legacyForkIdEnabled = legacyForkIdEnabled;
|
||||
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;
|
||||
|
||||
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.SubProtocol;
|
||||
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.messages.DisconnectMessage.DisconnectReason;
|
||||
@@ -154,6 +155,9 @@ public class NetworkRunner implements AutoCloseable {
|
||||
protocolManager.handleNewConnection(connection);
|
||||
});
|
||||
|
||||
network.subscribeConnectRequest(
|
||||
(peer, incoming) -> protocolManager.shouldConnect(peer, incoming));
|
||||
|
||||
network.subscribeDisconnect(
|
||||
(connection, disconnectReason, initiatedByPeer) -> {
|
||||
if (Collections.disjoint(
|
||||
@@ -170,6 +174,10 @@ public class NetworkRunner implements AutoCloseable {
|
||||
stop();
|
||||
}
|
||||
|
||||
public RlpxAgent getRlpxAgent() {
|
||||
return network.getRlpxAgent();
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
private NetworkBuilder networkProvider;
|
||||
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.connections.PeerConnection;
|
||||
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 java.io.IOException;
|
||||
@@ -52,6 +53,9 @@ public class NoopP2PNetwork implements P2PNetwork {
|
||||
@Override
|
||||
public void subscribeConnect(final ConnectCallback callback) {}
|
||||
|
||||
@Override
|
||||
public void subscribeConnectRequest(final ShouldConnectCallback callback) {}
|
||||
|
||||
@Override
|
||||
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.DisconnectCallback;
|
||||
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.wire.Capability;
|
||||
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 java.io.Closeable;
|
||||
@@ -83,8 +85,9 @@ public interface P2PNetwork extends Closeable {
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
@@ -108,7 +111,7 @@ public interface P2PNetwork extends Closeable {
|
||||
* disconnected even if it is not in the maintained peer list. See {@link
|
||||
* #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
|
||||
*/
|
||||
boolean removeMaintainedConnectionPeer(final Peer peer);
|
||||
@@ -149,4 +152,9 @@ public interface P2PNetwork extends Closeable {
|
||||
Optional<EnodeURL> getLocalEnode();
|
||||
|
||||
void updateNodeRecord();
|
||||
|
||||
default RlpxAgent getRlpxAgent() {
|
||||
return null;
|
||||
}
|
||||
;
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
*/
|
||||
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.wire.Capability;
|
||||
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.Message;
|
||||
@@ -58,6 +59,14 @@ public interface ProtocolManager extends AutoCloseable {
|
||||
*/
|
||||
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.
|
||||
*
|
||||
|
||||
@@ -14,15 +14,18 @@
|
||||
*/
|
||||
package org.hyperledger.besu.ethereum.p2p.peers;
|
||||
|
||||
import org.hyperledger.besu.ethereum.forkid.ForkId;
|
||||
import org.hyperledger.besu.plugin.data.EnodeURL;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
/** The default, basic representation of an Ethereum {@link Peer}. */
|
||||
public class DefaultPeer extends DefaultPeerId implements Peer {
|
||||
|
||||
private final EnodeURL enodeURL;
|
||||
private ForkId forkId;
|
||||
|
||||
protected DefaultPeer(final EnodeURL enodeURL) {
|
||||
super(enodeURL.getNodeId());
|
||||
@@ -60,6 +63,16 @@ public class DefaultPeer extends DefaultPeerId implements Peer {
|
||||
return enodeURL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<ForkId> getForkId() {
|
||||
return Optional.ofNullable(forkId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setForkId(final ForkId forkId) {
|
||||
this.forkId = forkId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
if (obj == null) {
|
||||
|
||||
@@ -15,9 +15,13 @@
|
||||
package org.hyperledger.besu.ethereum.p2p.peers;
|
||||
|
||||
import org.hyperledger.besu.crypto.SecureRandomProvider;
|
||||
import org.hyperledger.besu.ethereum.forkid.ForkId;
|
||||
import org.hyperledger.besu.plugin.data.EnodeURL;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
import org.ethereum.beacon.discovery.schema.NodeRecord;
|
||||
|
||||
public interface Peer extends PeerId {
|
||||
|
||||
@@ -47,4 +51,29 @@ public interface Peer extends PeerId {
|
||||
default String getEnodeURLString() {
|
||||
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.checkState;
|
||||
import static java.util.Objects.isNull;
|
||||
|
||||
import org.hyperledger.besu.crypto.SECPPublicKey;
|
||||
import org.hyperledger.besu.cryptoservices.NodeKey;
|
||||
import org.hyperledger.besu.ethereum.p2p.config.RlpxConfiguration;
|
||||
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.PeerConnectionEvents;
|
||||
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.NettyTLSConnectionInitializer;
|
||||
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.ShouldConnectCallback;
|
||||
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.services.MetricsSystem;
|
||||
import org.hyperledger.besu.plugin.services.metrics.Counter;
|
||||
import org.hyperledger.besu.util.Subscribers;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
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.AtomicReference;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
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.jetbrains.annotations.NotNull;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -65,23 +65,20 @@ public class RlpxAgent {
|
||||
private final PeerConnectionEvents connectionEvents;
|
||||
private final ConnectionInitializer connectionInitializer;
|
||||
private final Subscribers<ConnectCallback> connectSubscribers = Subscribers.create();
|
||||
|
||||
private final List<ShouldConnectCallback> connectRequestSubscribers = new ArrayList<>();
|
||||
private final PeerRlpxPermissions peerPermissions;
|
||||
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 stopped = new AtomicBoolean(false);
|
||||
|
||||
private final Counter connectedPeersCounter;
|
||||
private final int lowerBound;
|
||||
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(
|
||||
final LocalNode localNode,
|
||||
@@ -89,32 +86,17 @@ public class RlpxAgent {
|
||||
final ConnectionInitializer connectionInitializer,
|
||||
final PeerRlpxPermissions peerPermissions,
|
||||
final PeerPrivileges peerPrivileges,
|
||||
final int upperBoundConnections,
|
||||
final int lowerBoundConnections,
|
||||
final int maxRemotelyInitiatedConnections,
|
||||
final boolean randomPeerPriority,
|
||||
final MetricsSystem metricsSystem) {
|
||||
final int peersLowerBound,
|
||||
final Supplier<Stream<PeerConnection>> allConnectionsSupplier,
|
||||
final Supplier<Stream<PeerConnection>> allActiveConnectionsSupplier) {
|
||||
this.localNode = localNode;
|
||||
this.connectionEvents = connectionEvents;
|
||||
this.connectionInitializer = connectionInitializer;
|
||||
this.peerPermissions = peerPermissions;
|
||||
this.peerPrivileges = peerPrivileges;
|
||||
this.upperBoundConnections = upperBoundConnections;
|
||||
this.lowerBoundConnections = lowerBoundConnections;
|
||||
this.randomPeerPriority = randomPeerPriority;
|
||||
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);
|
||||
this.lowerBound = peersLowerBound;
|
||||
this.allConnectionsSupplier = allConnectionsSupplier;
|
||||
this.allActiveConnectionsSupplier = allActiveConnectionsSupplier;
|
||||
}
|
||||
|
||||
public static Builder builder() {
|
||||
@@ -156,50 +138,58 @@ public class RlpxAgent {
|
||||
return connectionInitializer.stop();
|
||||
}
|
||||
|
||||
public Stream<? extends PeerConnection> streamConnections() {
|
||||
return connectionsById.values().stream()
|
||||
.filter(RlpxConnection::isActive)
|
||||
.map(RlpxConnection::getPeerConnection);
|
||||
public Stream<PeerConnection> streamConnections() {
|
||||
try {
|
||||
return allConnectionsSupplier.get();
|
||||
} 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() {
|
||||
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) {
|
||||
if (!localNode.isReady()) {
|
||||
return;
|
||||
}
|
||||
peerStream
|
||||
.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 + "}";
|
||||
peerStream.forEach(this::connect);
|
||||
}
|
||||
|
||||
public void disconnect(final Bytes peerId, final DisconnectReason reason) {
|
||||
final RlpxConnection connection = connectionsById.remove(peerId);
|
||||
if (connection != null) {
|
||||
connection.disconnect(reason);
|
||||
try {
|
||||
allActiveConnectionsSupplier
|
||||
.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
|
||||
*
|
||||
@@ -224,82 +214,55 @@ public class RlpxAgent {
|
||||
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
|
||||
if (!peerPermissions.allowNewOutboundConnectionTo(peer)) {
|
||||
return CompletableFuture.failedFuture(peerPermissions.newOutboundConnectionException(peer));
|
||||
}
|
||||
|
||||
// Initiate connection or return existing connection
|
||||
final AtomicReference<CompletableFuture<PeerConnection>> connectionFuture =
|
||||
new AtomicReference<>();
|
||||
connectionsById.compute(
|
||||
peer.getId(),
|
||||
(id, existingConnection) -> {
|
||||
if (existingConnection != null && !existingConnection.isFailedOrDisconnected()) {
|
||||
// We're already connected or connecting
|
||||
connectionFuture.set(existingConnection.getFuture());
|
||||
return existingConnection;
|
||||
} else {
|
||||
// We're initiating a new connection
|
||||
final CompletableFuture<PeerConnection> future = initiateOutboundConnection(peer);
|
||||
connectionFuture.set(future);
|
||||
final RlpxConnection newConnection = RlpxConnection.outboundConnection(peer, future);
|
||||
newConnection.subscribeConnectionEstablished(
|
||||
(conn) -> {
|
||||
this.dispatchConnect(conn.getPeerConnection());
|
||||
this.enforceConnectionLimits();
|
||||
},
|
||||
(failedConn) -> cleanUpPeerConnection(failedConn.getId()));
|
||||
return newConnection;
|
||||
final CompletableFuture<PeerConnection> peerConnectionCompletableFuture;
|
||||
if (checkWhetherToConnect(peer, false)) {
|
||||
try {
|
||||
synchronized (this) {
|
||||
peerConnectionCompletableFuture =
|
||||
peersConnectingCache.get(
|
||||
peer.getId(), () -> createPeerConnectionCompletableFuture(peer));
|
||||
}
|
||||
} catch (final ExecutionException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
} else {
|
||||
final String errorMsg =
|
||||
"None of the ProtocolManagers wants to connect to peer " + peer.getId();
|
||||
LOG.trace(errorMsg);
|
||||
return CompletableFuture.failedFuture((new RuntimeException(errorMsg)));
|
||||
}
|
||||
|
||||
return peerConnectionCompletableFuture;
|
||||
}
|
||||
|
||||
@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();
|
||||
|
||||
return connectionFuture.get();
|
||||
private boolean checkWhetherToConnect(final Peer peer, final boolean incoming) {
|
||||
return connectRequestSubscribers.stream()
|
||||
.anyMatch(callback -> callback.shouldConnect(peer, incoming));
|
||||
}
|
||||
|
||||
private void setupListeners() {
|
||||
connectionInitializer.subscribeIncomingConnect(this::handleIncomingConnection);
|
||||
connectionEvents.subscribeDisconnect(this::handleDisconnect);
|
||||
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(
|
||||
final boolean permissionsRestricted, final Optional<List<Peer>> peers) {
|
||||
if (!permissionsRestricted) {
|
||||
@@ -307,23 +270,23 @@ public class RlpxAgent {
|
||||
return;
|
||||
}
|
||||
|
||||
final List<RlpxConnection> connectionsToCheck =
|
||||
peers
|
||||
.map(
|
||||
updatedPeers ->
|
||||
updatedPeers.stream()
|
||||
.map(peer -> connectionsById.get(peer.getId()))
|
||||
.filter(connection -> !isNull(connection))
|
||||
.collect(Collectors.toList()))
|
||||
.orElse(new ArrayList<>(connectionsById.values()));
|
||||
final Stream<PeerConnection> connectionsToCheck;
|
||||
if (peers.isPresent()) {
|
||||
final List<Bytes> changedPeersIds =
|
||||
peers.get().stream().map(p -> p.getId()).collect(Collectors.toList());
|
||||
connectionsToCheck =
|
||||
streamConnections().filter(c -> changedPeersIds.contains(c.getPeer().getId()));
|
||||
} else {
|
||||
connectionsToCheck = streamConnections();
|
||||
}
|
||||
|
||||
connectionsToCheck.forEach(
|
||||
connection -> {
|
||||
if (!peerPermissions.allowOngoingConnection(
|
||||
connection.getPeer(), connection.initiatedRemotely())) {
|
||||
connection.getPeer(), connection.inboundInitiated())) {
|
||||
LOG.debug(
|
||||
"Disconnecting from peer that is not permitted to maintain ongoing connection: {}",
|
||||
connection.getPeerConnection());
|
||||
connection);
|
||||
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) {
|
||||
final Peer peer = peerConnection.getPeer();
|
||||
// Deny connection if our local node isn't ready
|
||||
@@ -355,28 +322,7 @@ public class RlpxAgent {
|
||||
peerConnection.disconnect(DisconnectReason.UNKNOWN);
|
||||
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
|
||||
if (!peerPermissions.allowNewInboundConnectionFrom(peer)) {
|
||||
LOG.debug(
|
||||
@@ -385,173 +331,13 @@ public class RlpxAgent {
|
||||
return;
|
||||
}
|
||||
|
||||
// Track this new connection, deduplicating existing connection if necessary
|
||||
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()) {
|
||||
if (checkWhetherToConnect(peer, true)) {
|
||||
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 {
|
||||
return randomPeerPriority
|
||||
? compareByMaskedNodeId(a, b)
|
||||
: compareConnectionInitiationTimes(a, b);
|
||||
peerConnection.disconnect(DisconnectReason.UNKNOWN);
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
connectionEvents.subscribeMessage(capability, callback);
|
||||
}
|
||||
@@ -560,15 +346,27 @@ public class RlpxAgent {
|
||||
connectSubscribers.subscribe(callback);
|
||||
}
|
||||
|
||||
public void subscribeConnectRequest(final ShouldConnectCallback callback) {
|
||||
connectRequestSubscribers.add(callback);
|
||||
}
|
||||
|
||||
public void subscribeDisconnect(final DisconnectCallback callback) {
|
||||
connectionEvents.subscribeDisconnect(callback);
|
||||
}
|
||||
|
||||
private void dispatchConnect(final PeerConnection connection) {
|
||||
connectedPeersCounter.inc();
|
||||
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 {
|
||||
private NodeKey nodeKey;
|
||||
private LocalNode localNode;
|
||||
@@ -577,9 +375,11 @@ public class RlpxAgent {
|
||||
private PeerPermissions peerPermissions;
|
||||
private ConnectionInitializer connectionInitializer;
|
||||
private PeerConnectionEvents connectionEvents;
|
||||
private boolean randomPeerPriority;
|
||||
private MetricsSystem metricsSystem;
|
||||
private Optional<TLSConfiguration> p2pTLSConfiguration;
|
||||
private Supplier<Stream<PeerConnection>> allConnectionsSupplier;
|
||||
private Supplier<Stream<PeerConnection>> allActiveConnectionsSupplier;
|
||||
private int peersLowerBound;
|
||||
|
||||
private Builder() {}
|
||||
|
||||
@@ -616,11 +416,9 @@ public class RlpxAgent {
|
||||
connectionInitializer,
|
||||
rlpxPermissions,
|
||||
peerPrivileges,
|
||||
config.getPeerUpperBound(),
|
||||
config.getPeerLowerBound(),
|
||||
config.getMaxRemotelyInitiatedConnections(),
|
||||
randomPeerPriority,
|
||||
metricsSystem);
|
||||
peersLowerBound,
|
||||
allConnectionsSupplier,
|
||||
allActiveConnectionsSupplier);
|
||||
}
|
||||
|
||||
private void validate() {
|
||||
@@ -680,14 +478,26 @@ public class RlpxAgent {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder randomPeerPriority(final boolean randomPeerPriority) {
|
||||
this.randomPeerPriority = randomPeerPriority;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder p2pTLSConfiguration(final Optional<TLSConfiguration> p2pTLSConfiguration) {
|
||||
this.p2pTLSConfiguration = p2pTLSConfiguration;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,9 +32,7 @@ import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.google.common.base.MoreObjects;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -53,6 +51,10 @@ public abstract class AbstractPeerConnection implements PeerConnection {
|
||||
private final AtomicBoolean disconnected = new AtomicBoolean(false);
|
||||
protected final PeerConnectionEventDispatcher connectionEventDispatcher;
|
||||
private final LabelledMetric<Counter> outboundMessagesCounter;
|
||||
private final long initiatedAt;
|
||||
private final boolean inboundInitiated;
|
||||
private boolean statusSent;
|
||||
private boolean statusReceived;
|
||||
|
||||
protected AbstractPeerConnection(
|
||||
final Peer peer,
|
||||
@@ -62,7 +64,8 @@ public abstract class AbstractPeerConnection implements PeerConnection {
|
||||
final String connectionId,
|
||||
final CapabilityMultiplexer multiplexer,
|
||||
final PeerConnectionEventDispatcher connectionEventDispatcher,
|
||||
final LabelledMetric<Counter> outboundMessagesCounter) {
|
||||
final LabelledMetric<Counter> outboundMessagesCounter,
|
||||
final boolean inboundInitiated) {
|
||||
this.peer = peer;
|
||||
this.peerInfo = peerInfo;
|
||||
this.localAddress = localAddress;
|
||||
@@ -76,6 +79,10 @@ public abstract class AbstractPeerConnection implements PeerConnection {
|
||||
}
|
||||
this.connectionEventDispatcher = connectionEventDispatcher;
|
||||
this.outboundMessagesCounter = outboundMessagesCounter;
|
||||
this.inboundInitiated = inboundInitiated;
|
||||
this.initiatedAt = System.currentTimeMillis();
|
||||
|
||||
LOG.debug("New PeerConnection ({}) established with peer {}", this, peer.getId());
|
||||
}
|
||||
|
||||
@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
|
||||
// message and are waiting to close.
|
||||
closeConnectionImmediately();
|
||||
LOG.debug("Terminating connection {}, reason {}", System.identityHashCode(this), reason);
|
||||
LOG.debug("Terminating connection {}, reason {}", this, reason);
|
||||
}
|
||||
|
||||
protected abstract void closeConnectionImmediately();
|
||||
@@ -179,6 +186,16 @@ public abstract class AbstractPeerConnection implements PeerConnection {
|
||||
return remoteAddress;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getInitiatedAt() {
|
||||
return initiatedAt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean inboundInitiated() {
|
||||
return inboundInitiated;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (o == this) {
|
||||
@@ -197,14 +214,31 @@ public abstract class AbstractPeerConnection implements PeerConnection {
|
||||
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
|
||||
public String toString() {
|
||||
return MoreObjects.toStringHelper(this)
|
||||
.add("nodeId", peerInfo.getNodeId())
|
||||
.add("clientId", peerInfo.getClientId())
|
||||
.add(
|
||||
"caps",
|
||||
agreedCapabilities.stream().map(Capability::toString).collect(Collectors.joining(", ")))
|
||||
.toString();
|
||||
return "[Connection with hashCode "
|
||||
+ hashCode()
|
||||
+ " with peer "
|
||||
+ this.peer.getId()
|
||||
+ " inboundInitiated "
|
||||
+ inboundInitiated
|
||||
+ " initAt "
|
||||
+ initiatedAt
|
||||
+ "]";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,4 +122,20 @@ public interface PeerConnection {
|
||||
default EnodeURL getRemoteEnode() {
|
||||
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 FramerProvider framerProvider;
|
||||
private final boolean inboundInitiated;
|
||||
|
||||
AbstractHandshakeHandler(
|
||||
final List<SubProtocol> subProtocols,
|
||||
@@ -68,7 +69,8 @@ abstract class AbstractHandshakeHandler extends SimpleChannelInboundHandler<Byte
|
||||
final PeerConnectionEventDispatcher connectionEventDispatcher,
|
||||
final MetricsSystem metricsSystem,
|
||||
final HandshakerProvider handshakerProvider,
|
||||
final FramerProvider framerProvider) {
|
||||
final FramerProvider framerProvider,
|
||||
final boolean inboundInitiated) {
|
||||
this.subProtocols = subProtocols;
|
||||
this.localNode = localNode;
|
||||
this.expectedPeer = expectedPeer;
|
||||
@@ -77,6 +79,7 @@ abstract class AbstractHandshakeHandler extends SimpleChannelInboundHandler<Byte
|
||||
this.metricsSystem = metricsSystem;
|
||||
this.handshaker = handshakerProvider.buildInstance();
|
||||
this.framerProvider = framerProvider;
|
||||
this.inboundInitiated = inboundInitiated;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -117,7 +120,8 @@ abstract class AbstractHandshakeHandler extends SimpleChannelInboundHandler<Byte
|
||||
expectedPeer,
|
||||
connectionEventDispatcher,
|
||||
connectionFuture,
|
||||
metricsSystem);
|
||||
metricsSystem,
|
||||
inboundInitiated);
|
||||
|
||||
ctx.channel()
|
||||
.pipeline()
|
||||
|
||||
@@ -69,6 +69,7 @@ final class DeFramer extends ByteToMessageDecoder {
|
||||
// The peer we are expecting to connect to, if such a peer is known
|
||||
private final Optional<Peer> expectedPeer;
|
||||
private final List<SubProtocol> subProtocols;
|
||||
private final boolean inboundInitiated;
|
||||
private boolean hellosExchanged;
|
||||
private final LabelledMetric<Counter> outboundMessagesCounter;
|
||||
|
||||
@@ -79,13 +80,15 @@ final class DeFramer extends ByteToMessageDecoder {
|
||||
final Optional<Peer> expectedPeer,
|
||||
final PeerConnectionEventDispatcher connectionEventDispatcher,
|
||||
final CompletableFuture<PeerConnection> connectFuture,
|
||||
final MetricsSystem metricsSystem) {
|
||||
final MetricsSystem metricsSystem,
|
||||
final boolean inboundInitiated) {
|
||||
this.framer = framer;
|
||||
this.subProtocols = subProtocols;
|
||||
this.localNode = localNode;
|
||||
this.expectedPeer = expectedPeer;
|
||||
this.connectFuture = connectFuture;
|
||||
this.connectionEventDispatcher = connectionEventDispatcher;
|
||||
this.inboundInitiated = inboundInitiated;
|
||||
this.outboundMessagesCounter =
|
||||
metricsSystem.createLabelledCounter(
|
||||
BesuMetricCategory.NETWORK,
|
||||
@@ -140,7 +143,8 @@ final class DeFramer extends ByteToMessageDecoder {
|
||||
peerInfo,
|
||||
capabilityMultiplexer,
|
||||
connectionEventDispatcher,
|
||||
outboundMessagesCounter);
|
||||
outboundMessagesCounter,
|
||||
inboundInitiated);
|
||||
|
||||
// Check peer is who we expected
|
||||
if (expectedPeer.isPresent()
|
||||
|
||||
@@ -49,7 +49,8 @@ final class HandshakeHandlerInbound extends AbstractHandshakeHandler {
|
||||
connectionEventDispatcher,
|
||||
metricsSystem,
|
||||
handshakerProvider,
|
||||
framerProvider);
|
||||
framerProvider,
|
||||
true);
|
||||
handshaker.prepareResponder(nodeKey);
|
||||
}
|
||||
|
||||
|
||||
@@ -59,7 +59,8 @@ final class HandshakeHandlerOutbound extends AbstractHandshakeHandler {
|
||||
connectionEventDispatcher,
|
||||
metricsSystem,
|
||||
handshakerProvider,
|
||||
framerProvider);
|
||||
framerProvider,
|
||||
false);
|
||||
handshaker.prepareInitiator(
|
||||
nodeKey, SignatureAlgorithmFactory.getInstance().createPublicKey(peer.getId()));
|
||||
this.first = handshaker.firstMessage();
|
||||
|
||||
@@ -138,7 +138,7 @@ public class NettyConnectionInitializer
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> stop() {
|
||||
CompletableFuture<Void> stoppedFuture = new CompletableFuture<>();
|
||||
final CompletableFuture<Void> stoppedFuture = new CompletableFuture<>();
|
||||
if (!started.get() || !stopped.compareAndSet(false, true)) {
|
||||
stoppedFuture.completeExceptionally(
|
||||
new IllegalStateException("Illegal attempt to stop " + this.getClass().getSimpleName()));
|
||||
|
||||
@@ -43,7 +43,8 @@ final class NettyPeerConnection extends AbstractPeerConnection {
|
||||
final PeerInfo peerInfo,
|
||||
final CapabilityMultiplexer multiplexer,
|
||||
final PeerConnectionEventDispatcher connectionEventDispatcher,
|
||||
final LabelledMetric<Counter> outboundMessagesCounter) {
|
||||
final LabelledMetric<Counter> outboundMessagesCounter,
|
||||
final boolean inboundInitiated) {
|
||||
super(
|
||||
peer,
|
||||
peerInfo,
|
||||
@@ -52,7 +53,8 @@ final class NettyPeerConnection extends AbstractPeerConnection {
|
||||
ctx.channel().id().asLongText(),
|
||||
multiplexer,
|
||||
connectionEventDispatcher,
|
||||
outboundMessagesCounter);
|
||||
outboundMessagesCounter,
|
||||
inboundInitiated);
|
||||
|
||||
this.ctx = ctx;
|
||||
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.rlpx.RlpxAgent;
|
||||
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.MockSubProtocol;
|
||||
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.messages.DisconnectMessage.DisconnectReason;
|
||||
@@ -196,25 +195,8 @@ public final class DefaultP2PNetworkTest {
|
||||
maintainedPeers.add(peer);
|
||||
|
||||
// Don't connect to an already connected peer
|
||||
final CompletableFuture<PeerConnection> connectionFuture =
|
||||
CompletableFuture.completedFuture(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));
|
||||
when(rlpxAgent.streamActiveConnections())
|
||||
.thenReturn(Stream.of(MockPeerConnection.create(peer)));
|
||||
network.checkMaintainedConnectionPeers();
|
||||
verify(rlpxAgent, times(0)).connect(peer);
|
||||
}
|
||||
@@ -410,6 +392,8 @@ public final class DefaultP2PNetworkTest {
|
||||
.supportedCapabilities(Capability.create("eth", 63))
|
||||
.storageProvider(new InMemoryKeyValueStorageProvider())
|
||||
.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.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import io.vertx.core.Vertx;
|
||||
import org.assertj.core.api.Assertions;
|
||||
@@ -81,9 +82,12 @@ public class NetworkingServiceLifecycleTest {
|
||||
final Block blockMock = mock(Block.class);
|
||||
when(blockMock.getHash()).thenReturn(Hash.ZERO);
|
||||
when(blockchainMock.getGenesisBlock()).thenReturn(blockMock);
|
||||
builder.blockchain(blockchainMock);
|
||||
builder.blockNumberForks(Collections.emptyList());
|
||||
builder.timestampForks(Collections.emptyList());
|
||||
builder
|
||||
.blockchain(blockchainMock)
|
||||
.blockNumberForks(Collections.emptyList())
|
||||
.timestampForks(Collections.emptyList())
|
||||
.allConnectionsSupplier(Stream::empty)
|
||||
.allActiveConnectionsSupplier(Stream::empty);
|
||||
return builder;
|
||||
}
|
||||
|
||||
|
||||
@@ -49,6 +49,7 @@ import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import io.vertx.core.Vertx;
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
@@ -79,6 +80,8 @@ public class P2PNetworkTest {
|
||||
final NodeKey nodeKey = NodeKeyUtils.generate();
|
||||
try (final P2PNetwork listener = builder().nodeKey(nodeKey).build();
|
||||
final P2PNetwork connector = builder().build()) {
|
||||
listener.getRlpxAgent().subscribeConnectRequest((p, d) -> true);
|
||||
connector.getRlpxAgent().subscribeConnectRequest((p, d) -> true);
|
||||
|
||||
listener.start();
|
||||
connector.start();
|
||||
@@ -101,6 +104,8 @@ public class P2PNetworkTest {
|
||||
final NodeKey listenNodeKey = NodeKeyUtils.generate();
|
||||
try (final P2PNetwork listener = builder().nodeKey(listenNodeKey).build();
|
||||
final P2PNetwork connector = builder().build()) {
|
||||
listener.getRlpxAgent().subscribeConnectRequest((p, d) -> true);
|
||||
connector.getRlpxAgent().subscribeConnectRequest((p, d) -> true);
|
||||
|
||||
listener.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
|
||||
public void rejectPeerWithNoSharedCaps() throws Exception {
|
||||
final NodeKey listenerNodeKey = NodeKeyUtils.generate();
|
||||
@@ -197,6 +141,9 @@ public class P2PNetworkTest {
|
||||
builder().nodeKey(listenerNodeKey).supportedCapabilities(cap1).build();
|
||||
final P2PNetwork connector =
|
||||
builder().nodeKey(connectorNodeKey).supportedCapabilities(cap2).build()) {
|
||||
listener.getRlpxAgent().subscribeConnectRequest((p, d) -> true);
|
||||
connector.getRlpxAgent().subscribeConnectRequest((p, d) -> true);
|
||||
|
||||
listener.start();
|
||||
connector.start();
|
||||
final EnodeURL listenerEnode = listener.getLocalEnode().get();
|
||||
@@ -215,6 +162,8 @@ public class P2PNetworkTest {
|
||||
|
||||
try (final P2PNetwork localNetwork = builder().peerPermissions(localDenylist).build();
|
||||
final P2PNetwork remoteNetwork = builder().build()) {
|
||||
localNetwork.getRlpxAgent().subscribeConnectRequest((p, d) -> true);
|
||||
remoteNetwork.getRlpxAgent().subscribeConnectRequest((p, d) -> true);
|
||||
|
||||
localNetwork.start();
|
||||
remoteNetwork.start();
|
||||
@@ -262,6 +211,8 @@ public class P2PNetworkTest {
|
||||
|
||||
try (final P2PNetwork localNetwork = builder().peerPermissions(peerPermissions).build();
|
||||
final P2PNetwork remoteNetwork = builder().build()) {
|
||||
localNetwork.getRlpxAgent().subscribeConnectRequest((p, d) -> true);
|
||||
remoteNetwork.getRlpxAgent().subscribeConnectRequest((p, d) -> true);
|
||||
|
||||
localNetwork.start();
|
||||
remoteNetwork.start();
|
||||
@@ -323,6 +274,8 @@ public class P2PNetworkTest {
|
||||
.storageProvider(new InMemoryKeyValueStorageProvider())
|
||||
.blockNumberForks(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.Message;
|
||||
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.messages.DisconnectMessage.DisconnectReason;
|
||||
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
|
||||
@@ -58,6 +59,7 @@ import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import io.vertx.core.Vertx;
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
@@ -89,6 +91,8 @@ public class P2PPlainNetworkTest {
|
||||
final NodeKey nodeKey = NodeKeyUtils.generate();
|
||||
try (final P2PNetwork listener = builder("partner1client1").nodeKey(nodeKey).build();
|
||||
final P2PNetwork connector = builder("partner2client1").build()) {
|
||||
listener.getRlpxAgent().subscribeConnectRequest(testCallback);
|
||||
connector.getRlpxAgent().subscribeConnectRequest(testCallback);
|
||||
|
||||
listener.start();
|
||||
connector.start();
|
||||
@@ -111,6 +115,8 @@ public class P2PPlainNetworkTest {
|
||||
final NodeKey listenNodeKey = NodeKeyUtils.generate();
|
||||
try (final P2PNetwork listener = builder("partner1client1").nodeKey(listenNodeKey).build();
|
||||
final P2PNetwork connector = builder("partner2client1").build()) {
|
||||
listener.getRlpxAgent().subscribeConnectRequest(testCallback);
|
||||
connector.getRlpxAgent().subscribeConnectRequest(testCallback);
|
||||
|
||||
listener.start();
|
||||
connector.start();
|
||||
@@ -142,19 +148,20 @@ public class P2PPlainNetworkTest {
|
||||
@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("partner1client1").nodeKey(nodeKey).config(listenerConfig).build();
|
||||
final P2PNetwork connector1 = builder("partner1client1").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
|
||||
listener.start();
|
||||
@@ -181,17 +188,7 @@ public class P2PPlainNetworkTest {
|
||||
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);
|
||||
Assertions.assertThat(connector2.connect(listeningPeer)).isCompletedExceptionally();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -214,6 +211,9 @@ public class P2PPlainNetworkTest {
|
||||
.nodeKey(connectorNodeKey)
|
||||
.supportedCapabilities(cap2)
|
||||
.build()) {
|
||||
listener.getRlpxAgent().subscribeConnectRequest(testCallback);
|
||||
connector.getRlpxAgent().subscribeConnectRequest(testCallback);
|
||||
|
||||
listener.start();
|
||||
connector.start();
|
||||
final EnodeURL listenerEnode = listener.getLocalEnode().get();
|
||||
@@ -233,6 +233,8 @@ public class P2PPlainNetworkTest {
|
||||
try (final P2PNetwork localNetwork =
|
||||
builder("partner1client1").peerPermissions(localDenylist).build();
|
||||
final P2PNetwork remoteNetwork = builder("partner2client1").build()) {
|
||||
localNetwork.getRlpxAgent().subscribeConnectRequest(testCallback);
|
||||
remoteNetwork.getRlpxAgent().subscribeConnectRequest(testCallback);
|
||||
|
||||
localNetwork.start();
|
||||
remoteNetwork.start();
|
||||
@@ -281,6 +283,8 @@ public class P2PPlainNetworkTest {
|
||||
try (final P2PNetwork localNetwork =
|
||||
builder("partner1client1").peerPermissions(peerPermissions).build();
|
||||
final P2PNetwork remoteNetwork = builder("partner2client1").build()) {
|
||||
localNetwork.getRlpxAgent().subscribeConnectRequest(testCallback);
|
||||
remoteNetwork.getRlpxAgent().subscribeConnectRequest(testCallback);
|
||||
|
||||
localNetwork.start();
|
||||
remoteNetwork.start();
|
||||
@@ -330,6 +334,8 @@ public class P2PPlainNetworkTest {
|
||||
final NodeKey nodeKey = NodeKeyUtils.generate();
|
||||
try (final P2PNetwork listener = builder("partner1client1").nodeKey(nodeKey).build();
|
||||
final P2PNetwork connector = builder("partner2client1").build()) {
|
||||
listener.getRlpxAgent().subscribeConnectRequest(testCallback);
|
||||
connector.getRlpxAgent().subscribeConnectRequest(testCallback);
|
||||
|
||||
final CompletableFuture<DisconnectReason> disconnectReasonFuture = new CompletableFuture<>();
|
||||
listener.subscribeDisconnect(
|
||||
@@ -378,6 +384,8 @@ public class P2PPlainNetworkTest {
|
||||
}
|
||||
}
|
||||
|
||||
private final ShouldConnectCallback testCallback = (p, d) -> true;
|
||||
|
||||
private static class LargeMessageData extends AbstractMessageData {
|
||||
|
||||
public static final int VALID_ETH_MESSAGE_CODE = 0x07;
|
||||
@@ -410,7 +418,7 @@ public class P2PPlainNetworkTest {
|
||||
private static Path toPath(final String path) {
|
||||
try {
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -449,6 +457,8 @@ public class P2PPlainNetworkTest {
|
||||
.storageProvider(new InMemoryKeyValueStorageProvider())
|
||||
.blockNumberForks(Collections.emptyList())
|
||||
.timestampForks(Collections.emptyList())
|
||||
.blockchain(blockchainMock);
|
||||
.blockchain(blockchainMock)
|
||||
.allConnectionsSupplier(Stream::empty)
|
||||
.allActiveConnectionsSupplier(Stream::empty);
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -181,7 +181,8 @@ public class AbstractPeerConnectionTest {
|
||||
connectionId,
|
||||
multiplexer,
|
||||
connectionEventDispatcher,
|
||||
outboundMessagesCounter);
|
||||
outboundMessagesCounter,
|
||||
true);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -44,7 +44,7 @@ public class MockConnectionInitializer implements ConnectionInitializer {
|
||||
}
|
||||
|
||||
public void completePendingFutures() {
|
||||
for (Map.Entry<Peer, CompletableFuture<PeerConnection>> conn :
|
||||
for (final Map.Entry<Peer, CompletableFuture<PeerConnection>> conn :
|
||||
incompleteConnections.entrySet()) {
|
||||
conn.getValue().complete(MockPeerConnection.create(conn.getKey()));
|
||||
}
|
||||
@@ -57,7 +57,7 @@ public class MockConnectionInitializer implements ConnectionInitializer {
|
||||
|
||||
@Override
|
||||
public CompletableFuture<InetSocketAddress> start() {
|
||||
InetSocketAddress socketAddress =
|
||||
final InetSocketAddress socketAddress =
|
||||
new InetSocketAddress("127.0.0.1", NEXT_PORT.incrementAndGet());
|
||||
return CompletableFuture.completedFuture(socketAddress);
|
||||
}
|
||||
@@ -76,12 +76,14 @@ public class MockConnectionInitializer implements ConnectionInitializer {
|
||||
public CompletableFuture<PeerConnection> connect(final Peer peer) {
|
||||
if (autoDisconnectCounter > 0) {
|
||||
autoDisconnectCounter--;
|
||||
MockPeerConnection mockPeerConnection = MockPeerConnection.create(peer, eventDispatcher);
|
||||
final MockPeerConnection mockPeerConnection =
|
||||
MockPeerConnection.create(peer, eventDispatcher, false);
|
||||
mockPeerConnection.disconnect(DisconnectMessage.DisconnectReason.CLIENT_QUITTING);
|
||||
return CompletableFuture.completedFuture(mockPeerConnection);
|
||||
}
|
||||
if (autocompleteConnections) {
|
||||
return CompletableFuture.completedFuture(MockPeerConnection.create(peer, eventDispatcher));
|
||||
return CompletableFuture.completedFuture(
|
||||
MockPeerConnection.create(peer, eventDispatcher, false));
|
||||
} else {
|
||||
final CompletableFuture<PeerConnection> future = new CompletableFuture<>();
|
||||
incompleteConnections.put(peer, future);
|
||||
|
||||
@@ -47,7 +47,8 @@ public class MockPeerConnection extends AbstractPeerConnection {
|
||||
final String connectionId,
|
||||
final CapabilityMultiplexer multiplexer,
|
||||
final PeerConnectionEventDispatcher connectionEventDispatcher,
|
||||
final LabelledMetric<Counter> outboundMessagesCounter) {
|
||||
final LabelledMetric<Counter> outboundMessagesCounter,
|
||||
final boolean inboundInitiated) {
|
||||
super(
|
||||
peer,
|
||||
peerInfo,
|
||||
@@ -56,7 +57,8 @@ public class MockPeerConnection extends AbstractPeerConnection {
|
||||
connectionId,
|
||||
multiplexer,
|
||||
connectionEventDispatcher,
|
||||
outboundMessagesCounter);
|
||||
outboundMessagesCounter,
|
||||
inboundInitiated);
|
||||
}
|
||||
|
||||
public static MockPeerConnection create() {
|
||||
@@ -64,11 +66,13 @@ public class MockPeerConnection extends AbstractPeerConnection {
|
||||
}
|
||||
|
||||
public static MockPeerConnection create(final Peer peer) {
|
||||
return create(peer, mock(PeerConnectionEventDispatcher.class));
|
||||
return create(peer, mock(PeerConnectionEventDispatcher.class), true);
|
||||
}
|
||||
|
||||
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<Capability> caps = Arrays.asList(Capability.create("eth", 63));
|
||||
final CapabilityMultiplexer multiplexer = new CapabilityMultiplexer(subProtocols, caps, caps);
|
||||
@@ -83,7 +87,8 @@ public class MockPeerConnection extends AbstractPeerConnection {
|
||||
Integer.toString(connectionId.incrementAndGet()),
|
||||
multiplexer,
|
||||
eventDispatcher,
|
||||
NoOpMetricsSystem.NO_OP_LABELLED_3_COUNTER);
|
||||
NoOpMetricsSystem.NO_OP_LABELLED_3_COUNTER,
|
||||
inboundInitiated);
|
||||
}
|
||||
|
||||
@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),
|
||||
connectionEventDispatcher,
|
||||
connectFuture,
|
||||
new NoOpMetricsSystem());
|
||||
new NoOpMetricsSystem(),
|
||||
true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ public class InsufficientPeersPermissioningProvider implements ContextualNodePer
|
||||
@Override
|
||||
public Optional<Boolean> isPermitted(
|
||||
final EnodeURL sourceEnode, final EnodeURL destinationEnode) {
|
||||
Optional<EnodeURL> maybeSelfEnode = p2pNetwork.getLocalEnode();
|
||||
final Optional<EnodeURL> maybeSelfEnode = p2pNetwork.getLocalEnode();
|
||||
if (nonBootnodePeerConnections > 0) {
|
||||
return Optional.empty();
|
||||
} else if (!maybeSelfEnode.isPresent()) {
|
||||
|
||||
@@ -183,21 +183,21 @@ public class InsufficientPeersPermissioningProviderTest {
|
||||
ArgumentCaptor.forClass(ConnectCallback.class);
|
||||
|
||||
verify(p2pNetwork).subscribeConnect(callbackCaptor.capture());
|
||||
final ConnectCallback connectCallback = callbackCaptor.getValue();
|
||||
final ConnectCallback incomingConnectCallback = callbackCaptor.getValue();
|
||||
|
||||
final Runnable updatePermsCallback = mock(Runnable.class);
|
||||
|
||||
provider.subscribeToUpdates(updatePermsCallback);
|
||||
|
||||
connectCallback.onConnect(peerConnectionMatching(ENODE_2));
|
||||
incomingConnectCallback.onConnect(peerConnectionMatching(ENODE_2));
|
||||
verify(updatePermsCallback, times(0)).run();
|
||||
connectCallback.onConnect(peerConnectionMatching(ENODE_3));
|
||||
incomingConnectCallback.onConnect(peerConnectionMatching(ENODE_3));
|
||||
verify(updatePermsCallback, times(0)).run();
|
||||
connectCallback.onConnect(peerConnectionMatching(ENODE_4));
|
||||
incomingConnectCallback.onConnect(peerConnectionMatching(ENODE_4));
|
||||
verify(updatePermsCallback, times(1)).run();
|
||||
connectCallback.onConnect(peerConnectionMatching(ENODE_5));
|
||||
incomingConnectCallback.onConnect(peerConnectionMatching(ENODE_5));
|
||||
verify(updatePermsCallback, times(1)).run();
|
||||
connectCallback.onConnect(peerConnectionMatching(ENODE_3));
|
||||
incomingConnectCallback.onConnect(peerConnectionMatching(ENODE_3));
|
||||
verify(updatePermsCallback, times(1)).run();
|
||||
}
|
||||
|
||||
@@ -215,7 +215,7 @@ public class InsufficientPeersPermissioningProviderTest {
|
||||
final ArgumentCaptor<ConnectCallback> connectCallbackCaptor =
|
||||
ArgumentCaptor.forClass(ConnectCallback.class);
|
||||
verify(p2pNetwork).subscribeConnect(connectCallbackCaptor.capture());
|
||||
final ConnectCallback connectCallback = connectCallbackCaptor.getValue();
|
||||
final ConnectCallback incomingConnectCallback = connectCallbackCaptor.getValue();
|
||||
|
||||
final ArgumentCaptor<DisconnectCallback> disconnectCallbackCaptor =
|
||||
ArgumentCaptor.forClass(DisconnectCallback.class);
|
||||
@@ -226,13 +226,13 @@ public class InsufficientPeersPermissioningProviderTest {
|
||||
|
||||
provider.subscribeToUpdates(updatePermsCallback);
|
||||
|
||||
connectCallback.onConnect(peerConnectionMatching(ENODE_2));
|
||||
incomingConnectCallback.onConnect(peerConnectionMatching(ENODE_2));
|
||||
verify(updatePermsCallback, times(0)).run();
|
||||
connectCallback.onConnect(peerConnectionMatching(ENODE_3));
|
||||
incomingConnectCallback.onConnect(peerConnectionMatching(ENODE_3));
|
||||
verify(updatePermsCallback, times(0)).run();
|
||||
connectCallback.onConnect(peerConnectionMatching(ENODE_4));
|
||||
incomingConnectCallback.onConnect(peerConnectionMatching(ENODE_4));
|
||||
verify(updatePermsCallback, times(1)).run();
|
||||
connectCallback.onConnect(peerConnectionMatching(ENODE_5));
|
||||
incomingConnectCallback.onConnect(peerConnectionMatching(ENODE_5));
|
||||
verify(updatePermsCallback, times(1)).run();
|
||||
disconnectCallback.onDisconnect(peerConnectionMatching(ENODE_2), null, true);
|
||||
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.util.Subscribers;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.function.Supplier;
|
||||
@@ -83,6 +84,7 @@ public class RetestethContext {
|
||||
private static final PoWHasher NO_WORK_HASHER =
|
||||
(final long nonce, final long number, EpochCalculator epochCalc, final Bytes headerHash) ->
|
||||
new PoWSolution(nonce, Hash.ZERO, UInt256.ZERO, Hash.ZERO);
|
||||
public static final int MAX_PEERS = 25;
|
||||
|
||||
private final ReentrantLock contextLock = new ReentrantLock();
|
||||
private Address coinbase;
|
||||
@@ -198,6 +200,8 @@ public class RetestethContext {
|
||||
|
||||
blockReplay = new BlockReplay(protocolSchedule, blockchainQueries.getBlockchain());
|
||||
|
||||
final Bytes localNodeKey = Bytes.wrap(new byte[64]);
|
||||
|
||||
// mining support
|
||||
|
||||
final Supplier<ProtocolSpec> currentProtocolSpecSupplier =
|
||||
@@ -208,8 +212,13 @@ public class RetestethContext {
|
||||
currentProtocolSpecSupplier,
|
||||
retestethClock,
|
||||
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);
|
||||
|
||||
ethScheduler = new EthScheduler(1, 1, 1, 1, metricsSystem);
|
||||
|
||||
@@ -131,8 +131,7 @@ public class Subscribers<T> {
|
||||
action.accept(subscriber);
|
||||
} catch (final Exception e) {
|
||||
if (suppressCallbackExceptions) {
|
||||
LOG.info("Error in callback: {}", e.getMessage());
|
||||
LOG.debug("Error in callback: ", e);
|
||||
LOG.debug("Error in callback: {}", e);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user