diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ThreadBesuNodeRunner.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ThreadBesuNodeRunner.java index 13a298382e..267d6f1f97 100644 --- a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ThreadBesuNodeRunner.java +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ThreadBesuNodeRunner.java @@ -220,7 +220,6 @@ public class ThreadBesuNodeRunner implements BesuNodeRunner { .besuPluginContext(new BesuPluginContextImpl()) .autoLogBloomCaching(false) .storageProvider(storageProvider) - .forkIdSupplier(() -> besuController.getProtocolManager().getForkIdAsBytesList()) .rpcEndpointService(new RpcEndpointServiceImpl()); node.engineRpcConfiguration().ifPresent(runnerBuilder::engineJsonRpcConfiguration); diff --git a/besu/src/main/java/org/hyperledger/besu/RunnerBuilder.java b/besu/src/main/java/org/hyperledger/besu/RunnerBuilder.java index a14c4557ff..4aa35a08f0 100644 --- a/besu/src/main/java/org/hyperledger/besu/RunnerBuilder.java +++ b/besu/src/main/java/org/hyperledger/besu/RunnerBuilder.java @@ -133,7 +133,6 @@ import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -191,9 +190,9 @@ public class RunnerBuilder { private boolean autoLogBloomCaching = true; private boolean randomPeerPriority; private StorageProvider storageProvider; - private Supplier> forkIdSupplier; private RpcEndpointServiceImpl rpcEndpointServiceImpl; private JsonRpcIpcConfiguration jsonRpcIpcConfiguration; + private boolean legacyForkIdEnabled; public RunnerBuilder vertx(final Vertx vertx) { this.vertx = vertx; @@ -388,11 +387,6 @@ public class RunnerBuilder { return this; } - public RunnerBuilder forkIdSupplier(final Supplier> forkIdSupplier) { - this.forkIdSupplier = forkIdSupplier; - return this; - } - public RunnerBuilder rpcEndpointService(final RpcEndpointServiceImpl rpcEndpointService) { this.rpcEndpointServiceImpl = rpcEndpointService; return this; @@ -424,6 +418,8 @@ public class RunnerBuilder { discoveryConfiguration.setDnsDiscoveryURL(ethNetworkConfig.getDnsDiscoveryUrl()); discoveryConfiguration.setDiscoveryV5Enabled( networkingConfiguration.getDiscovery().isDiscoveryV5Enabled()); + discoveryConfiguration.setFilterOnEnrForkId( + networkingConfiguration.getDiscovery().isFilterOnEnrForkIdEnabled()); } else { discoveryConfiguration.setActive(false); } @@ -487,14 +483,16 @@ public class RunnerBuilder { .vertx(vertx) .nodeKey(nodeKey) .config(networkingConfiguration) + .legacyForkIdEnabled(legacyForkIdEnabled) .peerPermissions(peerPermissions) .metricsSystem(metricsSystem) .supportedCapabilities(caps) .natService(natService) .randomPeerPriority(randomPeerPriority) .storageProvider(storageProvider) - .forkIdSupplier(forkIdSupplier) .p2pTLSConfiguration(p2pTLSConfiguration) + .blockchain(context.getBlockchain()) + .forks(besuController.getGenesisConfigOptions().getForks()) .build(); final NetworkRunner networkRunner = @@ -663,7 +661,7 @@ public class RunnerBuilder { dataDir, rpcEndpointServiceImpl); - Optional authToUse = + final Optional authToUse = engineJsonRpcConfiguration.get().isAuthenticationEnabled() ? Optional.of( new EngineAuthService( @@ -673,7 +671,7 @@ public class RunnerBuilder { dataDir)) : Optional.empty(); - WebSocketConfiguration engineSocketConfig = + final WebSocketConfiguration engineSocketConfig = webSocketConfiguration.isEnabled() ? webSocketConfiguration : WebSocketConfiguration.createEngineDefault(); @@ -784,7 +782,8 @@ public class RunnerBuilder { createPrivateTransactionObserver(subscriptionManager, privacyParameters); } - Optional metricsService = createMetricsService(vertx, metricsConfiguration); + final Optional metricsService = + createMetricsService(vertx, metricsConfiguration); final Optional ethStatsService; if (!Strings.isNullOrEmpty(ethstatsUrl)) { @@ -807,7 +806,7 @@ public class RunnerBuilder { final Optional jsonRpcIpcService; if (jsonRpcIpcConfiguration.isEnabled()) { - Map ipcMethods = + final Map ipcMethods = jsonRpcMethods( protocolSchedule, context, @@ -1023,9 +1022,10 @@ public class RunnerBuilder { consensusEngineServer); methods.putAll(besuController.getAdditionalJsonRpcMethods(jsonRpcApis)); - var pluginMethods = rpcEndpointServiceImpl.getPluginMethods(jsonRpcConfiguration.getRpcApis()); + final var pluginMethods = + rpcEndpointServiceImpl.getPluginMethods(jsonRpcConfiguration.getRpcApis()); - var overriddenMethods = + final var overriddenMethods = methods.keySet().stream().filter(pluginMethods::containsKey).collect(Collectors.toList()); if (overriddenMethods.size() > 0) { throw new RuntimeException("You can not override built in methods " + overriddenMethods); @@ -1176,4 +1176,9 @@ public class RunnerBuilder { this.minPeers = minPeers; return this; } + + public RunnerBuilder legacyForkId(final boolean legacyEth64ForkIdEnabled) { + this.legacyForkIdEnabled = legacyEth64ForkIdEnabled; + return this; + } } diff --git a/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java b/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java index 7c008d4fc9..9369d9d77b 100644 --- a/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java +++ b/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java @@ -1425,7 +1425,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable { besuPluginContext.beforeExternalServices(); - var runner = buildRunner(); + final var runner = buildRunner(); runner.startExternalServices(); startPlugins(); @@ -1469,7 +1469,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable { final String generateCompletionSubcommandName = "generate-completion"; commandLine.addSubcommand( generateCompletionSubcommandName, AutoComplete.GenerateCompletion.class); - CommandLine generateCompletionSubcommand = + final CommandLine generateCompletionSubcommand = commandLine.getSubcommands().get(generateCompletionSubcommandName); generateCompletionSubcommand.getCommandSpec().usageMessage().hidden(true); } @@ -1849,7 +1849,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable { } public void validateRpcOptionsParams() { - Predicate configuredApis = + final Predicate configuredApis = apiName -> Arrays.stream(RpcApis.values()) .anyMatch(builtInApi -> apiName.equals(builtInApi.name())) @@ -2176,7 +2176,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable { private JsonRpcConfiguration createEngineJsonRpcConfiguration( final Integer listenPort, final List allowCallsFrom) { - JsonRpcConfiguration engineConfig = + final JsonRpcConfiguration engineConfig = jsonRpcConfiguration(listenPort, Arrays.asList("ENGINE", "ETH"), allowCallsFrom); engineConfig.setEnabled(isEngineApiEnabled()); if (!engineRPCOptionGroup.isEngineAuthDisabled) { @@ -2344,14 +2344,14 @@ public class BesuCommand implements DefaultCommandValues, Runnable { + ")"); } - for (String cipherSuite : jsonRPCHttpOptionGroup.rpcHttpTlsCipherSuites) { - if (!getJDKEnabledCypherSuites().contains(cipherSuite)) { + for (final String cipherSuite : jsonRPCHttpOptionGroup.rpcHttpTlsCipherSuites) { + if (!getJDKEnabledCipherSuites().contains(cipherSuite)) { throw new ParameterException( commandLine, "Invalid TLS cipher suite specified " + cipherSuite); } } - jsonRPCHttpOptionGroup.rpcHttpTlsCipherSuites.retainAll(getJDKEnabledCypherSuites()); + jsonRPCHttpOptionGroup.rpcHttpTlsCipherSuites.retainAll(getJDKEnabledCipherSuites()); return Optional.of( TlsConfiguration.Builder.aTlsConfiguration() @@ -2864,6 +2864,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable { .getValue()) .randomPeerPriority(p2PDiscoveryOptionGroup.randomPeerPriority) .networkingConfiguration(unstableNetworkingOptions.toDomainObject()) + .legacyForkId(unstableEthProtocolOptions.toDomainObject().isLegacyEth64ForkIdEnabled()) .graphQLConfiguration(graphQLConfiguration) .jsonRpcConfiguration(jsonRpcConfiguration) .engineJsonRpcConfiguration(engineJsonRpcConfiguration) @@ -2883,7 +2884,6 @@ public class BesuCommand implements DefaultCommandValues, Runnable { .ethstatsUrl(ethstatsOptions.getEthstatsUrl()) .ethstatsContact(ethstatsOptions.getEthstatsContact()) .storageProvider(keyValueStorageProvider(keyValueStorageName)) - .forkIdSupplier(() -> besuController.getProtocolManager().getForkIdAsBytesList()) .rpcEndpointService(rpcEndpointServiceImpl) .build(); @@ -3013,7 +3013,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable { try (final InputStream genesisFileInputStream = EthNetworkConfig.class.getResourceAsStream(networkName.getGenesisFile())) { return new String(genesisFileInputStream.readAllBytes(), UTF_8); - } catch (IOException | NullPointerException e) { + } catch (final IOException | NullPointerException e) { throw new IllegalStateException(e); } } @@ -3300,24 +3300,24 @@ public class BesuCommand implements DefaultCommandValues, Runnable { return engineRPCOptionGroup.overrideEngineRpcEnabled || isMergeEnabled(); } - public static List getJDKEnabledCypherSuites() { + public static List getJDKEnabledCipherSuites() { try { - SSLContext context = SSLContext.getInstance("TLS"); + final SSLContext context = SSLContext.getInstance("TLS"); context.init(null, null, null); - SSLEngine engine = context.createSSLEngine(); + final SSLEngine engine = context.createSSLEngine(); return Arrays.asList(engine.getEnabledCipherSuites()); - } catch (KeyManagementException | NoSuchAlgorithmException e) { + } catch (final KeyManagementException | NoSuchAlgorithmException e) { throw new RuntimeException(e); } } public static List getJDKEnabledProtocols() { try { - SSLContext context = SSLContext.getInstance("TLS"); + final SSLContext context = SSLContext.getInstance("TLS"); context.init(null, null, null); - SSLEngine engine = context.createSSLEngine(); + final SSLEngine engine = context.createSSLEngine(); return Arrays.asList(engine.getEnabledProtocols()); - } catch (KeyManagementException | NoSuchAlgorithmException e) { + } catch (final KeyManagementException | NoSuchAlgorithmException e) { throw new RuntimeException(e); } } diff --git a/besu/src/main/java/org/hyperledger/besu/cli/options/unstable/NetworkingOptions.java b/besu/src/main/java/org/hyperledger/besu/cli/options/unstable/NetworkingOptions.java index 88e899cac8..2f96a7fb59 100644 --- a/besu/src/main/java/org/hyperledger/besu/cli/options/unstable/NetworkingOptions.java +++ b/besu/src/main/java/org/hyperledger/besu/cli/options/unstable/NetworkingOptions.java @@ -33,6 +33,7 @@ public class NetworkingOptions implements CLIOptions { 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"; + public static final String FILTER_ON_ENR_FORK_ID = "--Xfilter-on-enr-fork-id"; @CommandLine.Option( names = INITIATE_CONNECTIONS_FREQUENCY_FLAG, @@ -68,6 +69,13 @@ public class NetworkingOptions implements CLIOptions { description = "Whether to enable P2P Discovery Protocol v5 (default: ${DEFAULT-VALUE})") private final Boolean isPeerDiscoveryV5Enabled = false; + @CommandLine.Option( + names = FILTER_ON_ENR_FORK_ID, + hidden = true, + defaultValue = "false", + description = "Whether to enable filtering of peers based on the ENR field ForkId)") + private final Boolean filterOnEnrForkId = false; + @CommandLine.Option( hidden = true, names = {P2P_PEER_LOWER_BOUND_FLAG}, @@ -99,6 +107,7 @@ public class NetworkingOptions implements CLIOptions { config.setInitiateConnectionsFrequency(initiateConnectionsFrequencySec); config.setDnsDiscoveryServerOverride(dnsDiscoveryServerOverride); config.getDiscovery().setDiscoveryV5Enabled(isPeerDiscoveryV5Enabled); + config.getDiscovery().setFilterOnEnrForkId(filterOnEnrForkId); config.getRlpx().setPeerLowerBound(peerLowerBound); return config; } diff --git a/besu/src/test/java/org/hyperledger/besu/ForkIdsTest.java b/besu/src/test/java/org/hyperledger/besu/ForkIdsTest.java index 40925768ad..4e0ee8f9e1 100644 --- a/besu/src/test/java/org/hyperledger/besu/ForkIdsTest.java +++ b/besu/src/test/java/org/hyperledger/besu/ForkIdsTest.java @@ -26,8 +26,8 @@ import org.hyperledger.besu.config.GenesisConfigFile; import org.hyperledger.besu.config.GenesisConfigOptions; import org.hyperledger.besu.ethereum.chain.Blockchain; import org.hyperledger.besu.ethereum.chain.GenesisState; -import org.hyperledger.besu.ethereum.eth.manager.ForkId; -import org.hyperledger.besu.ethereum.eth.manager.ForkIdManager; +import org.hyperledger.besu.ethereum.forkid.ForkId; +import org.hyperledger.besu.ethereum.forkid.ForkIdManager; import org.hyperledger.besu.ethereum.mainnet.MainnetProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.MutableProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; @@ -42,163 +42,177 @@ import java.util.stream.Stream; import com.google.common.collect.Streams; import org.apache.tuweni.bytes.Bytes; import org.junit.Test; +import org.junit.experimental.runners.Enclosed; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; -@RunWith(Parameterized.class) +@RunWith(Enclosed.class) public class ForkIdsTest { - @Parameterized.Parameter public NetworkName chainName; - - @Parameterized.Parameter(1) - public List expectedForkIds; - - @Parameterized.Parameters(name = "{0}") - public static Collection parameters() { - return List.of( - new Object[] { - NetworkName.SEPOLIA, - List.of( - new ForkId(Bytes.ofUnsignedInt(0xfe3366e7L), 1735371L), - new ForkId(Bytes.ofUnsignedInt(0xb96cbd13L), 0L), - new ForkId(Bytes.ofUnsignedInt(0xb96cbd13L), 0L)) - }, - new Object[] { - NetworkName.ROPSTEN, - List.of( - new ForkId(Bytes.ofUnsignedInt(0x30c7ddbcL), 10L), - new ForkId(Bytes.ofUnsignedInt(0x63760190L), 1700000L), - new ForkId(Bytes.ofUnsignedInt(0x3ea159c7L), 4230000L), - new ForkId(Bytes.ofUnsignedInt(0x97b544f3L), 4939394L), - new ForkId(Bytes.ofUnsignedInt(0xd6e2149bL), 6485846L), - new ForkId(Bytes.ofUnsignedInt(0x4bc66396L), 7117117L), - new ForkId(Bytes.ofUnsignedInt(0x6727ef90L), 9812189L), - new ForkId(Bytes.ofUnsignedInt(0xa157d377L), 10499401L), - new ForkId(Bytes.ofUnsignedInt(0x7119b6b3L), 0L), - new ForkId(Bytes.ofUnsignedInt(0x7119b6b3L), 0L)) - }, - new Object[] { - NetworkName.RINKEBY, - List.of( - new ForkId(Bytes.ofUnsignedInt(0x3b8e0691L), 1L), - new ForkId(Bytes.ofUnsignedInt(0x60949295L), 2L), - new ForkId(Bytes.ofUnsignedInt(0x8bde40ddL), 3L), - new ForkId(Bytes.ofUnsignedInt(0xcb3a64bbL), 1035301L), - new ForkId(Bytes.ofUnsignedInt(0x8d748b57L), 3660663L), - new ForkId(Bytes.ofUnsignedInt(0xe49cab14L), 4321234L), - new ForkId(Bytes.ofUnsignedInt(0xafec6b27L), 5435345L), - new ForkId(Bytes.ofUnsignedInt(0xcbdb8838L), 8290928L), - new ForkId(Bytes.ofUnsignedInt(0x6910c8bdL), 8897988L), - new ForkId(Bytes.ofUnsignedInt(0x8e29f2f3L), 0L), - new ForkId(Bytes.ofUnsignedInt(0x8e29f2f3L), 0L)) - }, - new Object[] { - NetworkName.GOERLI, - List.of( - new ForkId(Bytes.ofUnsignedInt(0xa3f5ab08L), 1561651L), - new ForkId(Bytes.ofUnsignedInt(0xc25efa5cL), 4460644L), - new ForkId(Bytes.ofUnsignedInt(0x757a1c47L), 5062605L), - new ForkId(Bytes.ofUnsignedInt(0xb8c6299dL), 0L), - new ForkId(Bytes.ofUnsignedInt(0xb8c6299dL), 0L)) - }, - new Object[] { - NetworkName.SHANDONG, - List.of( - new ForkId(Bytes.ofUnsignedInt(0x0459e09dL), 0L), - new ForkId(Bytes.ofUnsignedInt(0x0459e09dL), 0L)) - }, - new Object[] { - NetworkName.MAINNET, - List.of( - new ForkId(Bytes.ofUnsignedInt(0xfc64ec04L), 1150000L), - new ForkId(Bytes.ofUnsignedInt(0x97c2c34cL), 1920000L), - new ForkId(Bytes.ofUnsignedInt(0x91d1f948L), 2463000L), - new ForkId(Bytes.ofUnsignedInt(0x91d1f948L), 2463000L), - new ForkId(Bytes.ofUnsignedInt(0x91d1f948L), 2463000L), - new ForkId(Bytes.ofUnsignedInt(0x7a64da13L), 2675000L), - new ForkId(Bytes.ofUnsignedInt(0x3edd5b10L), 4370000L), - new ForkId(Bytes.ofUnsignedInt(0xa00bc324L), 7280000L), - new ForkId(Bytes.ofUnsignedInt(0x668db0afL), 9069000L), - new ForkId(Bytes.ofUnsignedInt(0x879d6e30L), 9200000L), - new ForkId(Bytes.ofUnsignedInt(0xe029e991L), 12244000L), - new ForkId(Bytes.ofUnsignedInt(0xeb440f6L), 12965000L), - new ForkId(Bytes.ofUnsignedInt(0xb715077dL), 13773000L), - new ForkId(Bytes.ofUnsignedInt(0x20c327fcL), 15050000L), - new ForkId(Bytes.ofUnsignedInt(0xf0afd0e3L), 0L), - new ForkId(Bytes.ofUnsignedInt(0xf0afd0e3L), 0L)) - }, - new Object[] { - NetworkName.MORDOR, - List.of( - new ForkId(Bytes.ofUnsignedInt(0x175782aaL), 301243L), - new ForkId(Bytes.ofUnsignedInt(0x604f6ee1L), 999983L), - new ForkId(Bytes.ofUnsignedInt(0xf42f5539L), 2520000L), - new ForkId(Bytes.ofUnsignedInt(0x66b5c286L), 3985893), - new ForkId(Bytes.ofUnsignedInt(0x92b323e0L), 5520000L), - new ForkId(Bytes.ofUnsignedInt(0x8c9b1797L), 0L), - new ForkId(Bytes.ofUnsignedInt(0x8c9b1797L), 0L)) - }, - new Object[] { - NetworkName.KOTTI, - List.of( - new ForkId(Bytes.ofUnsignedInt(0x550152eL), 716617L), - new ForkId(Bytes.ofUnsignedInt(0xa3270822L), 1705549L), - new ForkId(Bytes.ofUnsignedInt(0x8f3698e0L), 2200013L), - new ForkId(Bytes.ofUnsignedInt(0x6f402821L), 4368634), - new ForkId(Bytes.ofUnsignedInt(0xf03e54e7L), 5578000L), - new ForkId(Bytes.ofUnsignedInt(0xc5459816L), 0L), - new ForkId(Bytes.ofUnsignedInt(0xc5459816L), 0L)) - }, - new Object[] { - NetworkName.CLASSIC, - List.of( - new ForkId(Bytes.ofUnsignedInt(0xfc64ec04L), 1150000L), - new ForkId(Bytes.ofUnsignedInt(0x97c2c34cL), 2500000L), - new ForkId(Bytes.ofUnsignedInt(0x97c2c34cL), 2500000L), - new ForkId(Bytes.ofUnsignedInt(0x97c2c34cL), 2500000L), - new ForkId(Bytes.ofUnsignedInt(0xdb06803fL), 3000000L), - new ForkId(Bytes.ofUnsignedInt(0xaff4bed4L), 5000000L), - new ForkId(Bytes.ofUnsignedInt(0xf79a63c0L), 5900000L), - new ForkId(Bytes.ofUnsignedInt(0x744899d6L), 8772000L), - new ForkId(Bytes.ofUnsignedInt(0x518b59c6L), 9573000L), - new ForkId(Bytes.ofUnsignedInt(0x7ba22882L), 10500839L), - new ForkId(Bytes.ofUnsignedInt(0x9007bfccL), 11700000L), - new ForkId(Bytes.ofUnsignedInt(0xdb63a1caL), 13189133), - new ForkId(Bytes.ofUnsignedInt(0x0f6bf187L), 14525000L), - new ForkId(Bytes.ofUnsignedInt(0x7fd1bb25L), 0L), - new ForkId(Bytes.ofUnsignedInt(0x7fd1bb25L), 0L)) - }); + public static class NotParameterized { + @Test + public void testFromRaw() { + final ForkId forkId = new ForkId(Bytes.ofUnsignedInt(0xfe3366e7L), 1735371L); + final List> forkIdAsBytesList = List.of(forkId.getForkIdAsBytesList()); + assertThat(ForkId.fromRawForkId(forkIdAsBytesList).get()).isEqualTo(forkId); + } } - @Test - public void testForkId() { - final GenesisConfigFile genesisConfigFile = - GenesisConfigFile.fromConfig(EthNetworkConfig.jsonConfig(chainName)); - final GenesisConfigOptions configOptions = genesisConfigFile.getConfigOptions(); - final ProtocolSchedule schedule = - MainnetProtocolSchedule.fromConfig(configOptions, EvmConfiguration.DEFAULT); - final GenesisState genesisState = GenesisState.fromConfig(genesisConfigFile, schedule); - final Blockchain mockBlockchain = mock(Blockchain.class); - - when(mockBlockchain.getGenesisBlock()).thenReturn(genesisState.getBlock()); - - final AtomicLong blockNumber = new AtomicLong(); - when(mockBlockchain.getChainHeadBlockNumber()).thenAnswer(o -> blockNumber.get()); - - final ForkIdManager forkIdManager = - new ForkIdManager(mockBlockchain, genesisConfigFile.getForks(), false); - - final var actualForkIds = - Streams.concat( - ((MutableProtocolSchedule) schedule).streamMilestoneBlocks(), - Stream.of(Long.MAX_VALUE)) - .map( - block -> { - blockNumber.set(block); - return forkIdManager.getForkIdForChainHead(); - }) - .collect(Collectors.toList()); - - assertThat(actualForkIds).containsExactlyElementsOf(expectedForkIds); + @RunWith(Parameterized.class) + public static class ParametrizedForkIdTest { + + @Parameterized.Parameter public NetworkName chainName; + + @Parameterized.Parameter(1) + public List expectedForkIds; + + @Parameterized.Parameters(name = "{0}") + public static Collection parameters() { + return List.of( + new Object[] { + NetworkName.SEPOLIA, + List.of( + new ForkId(Bytes.ofUnsignedInt(0xfe3366e7L), 1735371L), + new ForkId(Bytes.ofUnsignedInt(0xb96cbd13L), 0L), + new ForkId(Bytes.ofUnsignedInt(0xb96cbd13L), 0L)) + }, + new Object[] { + NetworkName.ROPSTEN, + List.of( + new ForkId(Bytes.ofUnsignedInt(0x30c7ddbcL), 10L), + new ForkId(Bytes.ofUnsignedInt(0x63760190L), 1700000L), + new ForkId(Bytes.ofUnsignedInt(0x3ea159c7L), 4230000L), + new ForkId(Bytes.ofUnsignedInt(0x97b544f3L), 4939394L), + new ForkId(Bytes.ofUnsignedInt(0xd6e2149bL), 6485846L), + new ForkId(Bytes.ofUnsignedInt(0x4bc66396L), 7117117L), + new ForkId(Bytes.ofUnsignedInt(0x6727ef90L), 9812189L), + new ForkId(Bytes.ofUnsignedInt(0xa157d377L), 10499401L), + new ForkId(Bytes.ofUnsignedInt(0x7119b6b3L), 0L), + new ForkId(Bytes.ofUnsignedInt(0x7119b6b3L), 0L)) + }, + new Object[] { + NetworkName.RINKEBY, + List.of( + new ForkId(Bytes.ofUnsignedInt(0x3b8e0691L), 1L), + new ForkId(Bytes.ofUnsignedInt(0x60949295L), 2L), + new ForkId(Bytes.ofUnsignedInt(0x8bde40ddL), 3L), + new ForkId(Bytes.ofUnsignedInt(0xcb3a64bbL), 1035301L), + new ForkId(Bytes.ofUnsignedInt(0x8d748b57L), 3660663L), + new ForkId(Bytes.ofUnsignedInt(0xe49cab14L), 4321234L), + new ForkId(Bytes.ofUnsignedInt(0xafec6b27L), 5435345L), + new ForkId(Bytes.ofUnsignedInt(0xcbdb8838L), 8290928L), + new ForkId(Bytes.ofUnsignedInt(0x6910c8bdL), 8897988L), + new ForkId(Bytes.ofUnsignedInt(0x8e29f2f3L), 0L), + new ForkId(Bytes.ofUnsignedInt(0x8e29f2f3L), 0L)) + }, + new Object[] { + NetworkName.GOERLI, + List.of( + new ForkId(Bytes.ofUnsignedInt(0xa3f5ab08L), 1561651L), + new ForkId(Bytes.ofUnsignedInt(0xc25efa5cL), 4460644L), + new ForkId(Bytes.ofUnsignedInt(0x757a1c47L), 5062605L), + new ForkId(Bytes.ofUnsignedInt(0xb8c6299dL), 0L), + new ForkId(Bytes.ofUnsignedInt(0xb8c6299dL), 0L)) + }, + new Object[] { + NetworkName.SHANDONG, + List.of( + new ForkId(Bytes.ofUnsignedInt(0x0459e09dL), 0L), + new ForkId(Bytes.ofUnsignedInt(0x0459e09dL), 0L)) + }, + new Object[] { + NetworkName.MAINNET, + List.of( + new ForkId(Bytes.ofUnsignedInt(0xfc64ec04L), 1150000L), + new ForkId(Bytes.ofUnsignedInt(0x97c2c34cL), 1920000L), + new ForkId(Bytes.ofUnsignedInt(0x91d1f948L), 2463000L), + new ForkId(Bytes.ofUnsignedInt(0x91d1f948L), 2463000L), + new ForkId(Bytes.ofUnsignedInt(0x91d1f948L), 2463000L), + new ForkId(Bytes.ofUnsignedInt(0x7a64da13L), 2675000L), + new ForkId(Bytes.ofUnsignedInt(0x3edd5b10L), 4370000L), + new ForkId(Bytes.ofUnsignedInt(0xa00bc324L), 7280000L), + new ForkId(Bytes.ofUnsignedInt(0x668db0afL), 9069000L), + new ForkId(Bytes.ofUnsignedInt(0x879d6e30L), 9200000L), + new ForkId(Bytes.ofUnsignedInt(0xe029e991L), 12244000L), + new ForkId(Bytes.ofUnsignedInt(0xeb440f6L), 12965000L), + new ForkId(Bytes.ofUnsignedInt(0xb715077dL), 13773000L), + new ForkId(Bytes.ofUnsignedInt(0x20c327fcL), 15050000L), + new ForkId(Bytes.ofUnsignedInt(0xf0afd0e3L), 0L), + new ForkId(Bytes.ofUnsignedInt(0xf0afd0e3L), 0L)) + }, + new Object[] { + NetworkName.MORDOR, + List.of( + new ForkId(Bytes.ofUnsignedInt(0x175782aaL), 301243L), + new ForkId(Bytes.ofUnsignedInt(0x604f6ee1L), 999983L), + new ForkId(Bytes.ofUnsignedInt(0xf42f5539L), 2520000L), + new ForkId(Bytes.ofUnsignedInt(0x66b5c286L), 3985893), + new ForkId(Bytes.ofUnsignedInt(0x92b323e0L), 5520000L), + new ForkId(Bytes.ofUnsignedInt(0x8c9b1797L), 0L), + new ForkId(Bytes.ofUnsignedInt(0x8c9b1797L), 0L)) + }, + new Object[] { + NetworkName.KOTTI, + List.of( + new ForkId(Bytes.ofUnsignedInt(0x550152eL), 716617L), + new ForkId(Bytes.ofUnsignedInt(0xa3270822L), 1705549L), + new ForkId(Bytes.ofUnsignedInt(0x8f3698e0L), 2200013L), + new ForkId(Bytes.ofUnsignedInt(0x6f402821L), 4368634), + new ForkId(Bytes.ofUnsignedInt(0xf03e54e7L), 5578000L), + new ForkId(Bytes.ofUnsignedInt(0xc5459816L), 0L), + new ForkId(Bytes.ofUnsignedInt(0xc5459816L), 0L)) + }, + new Object[] { + NetworkName.CLASSIC, + List.of( + new ForkId(Bytes.ofUnsignedInt(0xfc64ec04L), 1150000L), + new ForkId(Bytes.ofUnsignedInt(0x97c2c34cL), 2500000L), + new ForkId(Bytes.ofUnsignedInt(0x97c2c34cL), 2500000L), + new ForkId(Bytes.ofUnsignedInt(0x97c2c34cL), 2500000L), + new ForkId(Bytes.ofUnsignedInt(0xdb06803fL), 3000000L), + new ForkId(Bytes.ofUnsignedInt(0xaff4bed4L), 5000000L), + new ForkId(Bytes.ofUnsignedInt(0xf79a63c0L), 5900000L), + new ForkId(Bytes.ofUnsignedInt(0x744899d6L), 8772000L), + new ForkId(Bytes.ofUnsignedInt(0x518b59c6L), 9573000L), + new ForkId(Bytes.ofUnsignedInt(0x7ba22882L), 10500839L), + new ForkId(Bytes.ofUnsignedInt(0x9007bfccL), 11700000L), + new ForkId(Bytes.ofUnsignedInt(0xdb63a1caL), 13189133), + new ForkId(Bytes.ofUnsignedInt(0x0f6bf187L), 14525000L), + new ForkId(Bytes.ofUnsignedInt(0x7fd1bb25L), 0L), + new ForkId(Bytes.ofUnsignedInt(0x7fd1bb25L), 0L)) + }); + } + + @Test + public void testForkId() { + final GenesisConfigFile genesisConfigFile = + GenesisConfigFile.fromConfig(EthNetworkConfig.jsonConfig(chainName)); + final GenesisConfigOptions configOptions = genesisConfigFile.getConfigOptions(); + final ProtocolSchedule schedule = + MainnetProtocolSchedule.fromConfig(configOptions, EvmConfiguration.DEFAULT); + final GenesisState genesisState = GenesisState.fromConfig(genesisConfigFile, schedule); + final Blockchain mockBlockchain = mock(Blockchain.class); + + when(mockBlockchain.getGenesisBlock()).thenReturn(genesisState.getBlock()); + + final AtomicLong blockNumber = new AtomicLong(); + when(mockBlockchain.getChainHeadBlockNumber()).thenAnswer(o -> blockNumber.get()); + + final ForkIdManager forkIdManager = + new ForkIdManager(mockBlockchain, genesisConfigFile.getForks(), false); + + final var actualForkIds = + Streams.concat( + ((MutableProtocolSchedule) schedule).streamMilestoneBlocks(), + Stream.of(Long.MAX_VALUE)) + .map( + block -> { + blockNumber.set(block); + return forkIdManager.getForkIdForChainHead(); + }) + .collect(Collectors.toList()); + + assertThat(actualForkIds).containsExactlyElementsOf(expectedForkIds); + } } } diff --git a/besu/src/test/java/org/hyperledger/besu/RunnerBuilderTest.java b/besu/src/test/java/org/hyperledger/besu/RunnerBuilderTest.java index 4db0439849..8a1b486b7c 100644 --- a/besu/src/test/java/org/hyperledger/besu/RunnerBuilderTest.java +++ b/besu/src/test/java/org/hyperledger/besu/RunnerBuilderTest.java @@ -25,6 +25,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import org.hyperledger.besu.cli.config.EthNetworkConfig; +import org.hyperledger.besu.config.GenesisConfigOptions; import org.hyperledger.besu.config.MergeConfigOptions; import org.hyperledger.besu.consensus.common.bft.BftEventQueue; import org.hyperledger.besu.consensus.common.bft.network.PeerConnectionTracker; @@ -36,6 +37,7 @@ import org.hyperledger.besu.controller.BesuController; import org.hyperledger.besu.crypto.KeyPairSecurityModule; import org.hyperledger.besu.crypto.NodeKey; import org.hyperledger.besu.crypto.SECP256K1; +import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.ethereum.ProtocolContext; import org.hyperledger.besu.ethereum.api.graphql.GraphQLConfiguration; import org.hyperledger.besu.ethereum.api.jsonrpc.JsonRpcConfiguration; @@ -116,7 +118,11 @@ public final class RunnerBuilderTest { when(ethProtocolManager.ethContext()).thenReturn(ethContext); when(subProtocolConfiguration.getSubProtocols()) .thenReturn(Collections.singletonList(new IbftSubProtocol())); - when(protocolContext.getBlockchain()).thenReturn(mock(DefaultBlockchain.class)); + final DefaultBlockchain blockchain = mock(DefaultBlockchain.class); + when(protocolContext.getBlockchain()).thenReturn(blockchain); + final Block block = mock(Block.class); + when(blockchain.getGenesisBlock()).thenReturn(block); + when(block.getHash()).thenReturn(Hash.ZERO); when(besuController.getProtocolManager()).thenReturn(ethProtocolManager); when(besuController.getSubProtocolConfiguration()).thenReturn(subProtocolConfiguration); @@ -128,6 +134,10 @@ public final class RunnerBuilderTest { when(besuController.getTransactionPool()).thenReturn(mock(TransactionPool.class)); when(besuController.getSynchronizer()).thenReturn(mock(Synchronizer.class)); when(besuController.getMiningCoordinator()).thenReturn(mock(MiningCoordinator.class)); + when(besuController.getMiningCoordinator()).thenReturn(mock(MergeMiningCoordinator.class)); + final GenesisConfigOptions genesisConfigOptions = mock(GenesisConfigOptions.class); + when(genesisConfigOptions.getForks()).thenReturn(Collections.emptyList()); + when(besuController.getGenesisConfigOptions()).thenReturn(genesisConfigOptions); } @Test @@ -154,7 +164,6 @@ public final class RunnerBuilderTest { .vertx(vertx) .dataDir(dataDir.getRoot().toPath()) .storageProvider(mock(KeyValueStorageProvider.class)) - .forkIdSupplier(() -> Collections.singletonList(Bytes.EMPTY)) .rpcEndpointService(new RpcEndpointServiceImpl()) .build(); runner.startEthereumMainLoop(); @@ -199,7 +208,6 @@ public final class RunnerBuilderTest { .vertx(Vertx.vertx()) .dataDir(dataDir.getRoot().toPath()) .storageProvider(storageProvider) - .forkIdSupplier(() -> Collections.singletonList(Bytes.EMPTY)) .rpcEndpointService(new RpcEndpointServiceImpl()) .build(); runner.startEthereumMainLoop(); @@ -225,11 +233,11 @@ public final class RunnerBuilderTest { @Test public void whenEngineApiAddedListensOnDefaultPort() { - JsonRpcConfiguration jrpc = JsonRpcConfiguration.createDefault(); + final JsonRpcConfiguration jrpc = JsonRpcConfiguration.createDefault(); jrpc.setEnabled(true); - JsonRpcConfiguration engine = JsonRpcConfiguration.createEngineDefault(); + final JsonRpcConfiguration engine = JsonRpcConfiguration.createEngineDefault(); engine.setEnabled(true); - EthNetworkConfig mockMainnet = mock(EthNetworkConfig.class); + final EthNetworkConfig mockMainnet = mock(EthNetworkConfig.class); when(mockMainnet.getNetworkId()).thenReturn(BigInteger.ONE); MergeConfigOptions.setMergeEnabled(true); when(besuController.getMiningCoordinator()).thenReturn(mock(MergeMiningCoordinator.class)); @@ -255,7 +263,6 @@ public final class RunnerBuilderTest { .vertx(Vertx.vertx()) .dataDir(dataDir.getRoot().toPath()) .storageProvider(mock(KeyValueStorageProvider.class)) - .forkIdSupplier(() -> Collections.singletonList(Bytes.EMPTY)) .rpcEndpointService(new RpcEndpointServiceImpl()) .besuPluginContext(mock(BesuPluginContextImpl.class)) .build(); @@ -266,13 +273,13 @@ public final class RunnerBuilderTest { @Test public void whenEngineApiAddedWebSocketReadyOnSamePort() { - WebSocketConfiguration wsRpc = WebSocketConfiguration.createDefault(); + final WebSocketConfiguration wsRpc = WebSocketConfiguration.createDefault(); wsRpc.setEnabled(true); - EthNetworkConfig mockMainnet = mock(EthNetworkConfig.class); + final EthNetworkConfig mockMainnet = mock(EthNetworkConfig.class); when(mockMainnet.getNetworkId()).thenReturn(BigInteger.ONE); MergeConfigOptions.setMergeEnabled(true); when(besuController.getMiningCoordinator()).thenReturn(mock(MergeMiningCoordinator.class)); - JsonRpcConfiguration engineConf = JsonRpcConfiguration.createEngineDefault(); + final JsonRpcConfiguration engineConf = JsonRpcConfiguration.createEngineDefault(); engineConf.setEnabled(true); final Runner runner = @@ -296,7 +303,6 @@ public final class RunnerBuilderTest { .vertx(Vertx.vertx()) .dataDir(dataDir.getRoot().toPath()) .storageProvider(mock(KeyValueStorageProvider.class)) - .forkIdSupplier(() -> Collections.singletonList(Bytes.EMPTY)) .rpcEndpointService(new RpcEndpointServiceImpl()) .besuPluginContext(mock(BesuPluginContextImpl.class)) .build(); @@ -306,13 +312,13 @@ public final class RunnerBuilderTest { @Test public void whenEngineApiAddedEthSubscribeAvailable() { - WebSocketConfiguration wsRpc = WebSocketConfiguration.createDefault(); + final WebSocketConfiguration wsRpc = WebSocketConfiguration.createDefault(); wsRpc.setEnabled(true); - EthNetworkConfig mockMainnet = mock(EthNetworkConfig.class); + final EthNetworkConfig mockMainnet = mock(EthNetworkConfig.class); when(mockMainnet.getNetworkId()).thenReturn(BigInteger.ONE); MergeConfigOptions.setMergeEnabled(true); when(besuController.getMiningCoordinator()).thenReturn(mock(MergeMiningCoordinator.class)); - JsonRpcConfiguration engineConf = JsonRpcConfiguration.createEngineDefault(); + final JsonRpcConfiguration engineConf = JsonRpcConfiguration.createEngineDefault(); engineConf.setEnabled(true); final Runner runner = @@ -336,7 +342,6 @@ public final class RunnerBuilderTest { .vertx(Vertx.vertx()) .dataDir(dataDir.getRoot().toPath()) .storageProvider(mock(KeyValueStorageProvider.class)) - .forkIdSupplier(() -> Collections.singletonList(Bytes.EMPTY)) .rpcEndpointService(new RpcEndpointServiceImpl()) .besuPluginContext(mock(BesuPluginContextImpl.class)) .build(); @@ -349,14 +354,13 @@ public final class RunnerBuilderTest { @Test public void noEngineApiNoServiceForMethods() { - JsonRpcConfiguration defaultRpcConfig = JsonRpcConfiguration.createDefault(); + final JsonRpcConfiguration defaultRpcConfig = JsonRpcConfiguration.createDefault(); defaultRpcConfig.setEnabled(true); - WebSocketConfiguration defaultWebSockConfig = WebSocketConfiguration.createDefault(); + final WebSocketConfiguration defaultWebSockConfig = WebSocketConfiguration.createDefault(); defaultWebSockConfig.setEnabled(true); - EthNetworkConfig mockMainnet = mock(EthNetworkConfig.class); + final EthNetworkConfig mockMainnet = mock(EthNetworkConfig.class); when(mockMainnet.getNetworkId()).thenReturn(BigInteger.ONE); MergeConfigOptions.setMergeEnabled(true); - when(besuController.getMiningCoordinator()).thenReturn(mock(MergeMiningCoordinator.class)); final Runner runner = new RunnerBuilder() @@ -378,7 +382,6 @@ public final class RunnerBuilderTest { .vertx(Vertx.vertx()) .dataDir(dataDir.getRoot().toPath()) .storageProvider(mock(KeyValueStorageProvider.class)) - .forkIdSupplier(() -> Collections.singletonList(Bytes.EMPTY)) .rpcEndpointService(new RpcEndpointServiceImpl()) .besuPluginContext(mock(BesuPluginContextImpl.class)) .build(); @@ -389,16 +392,16 @@ public final class RunnerBuilderTest { @Test public void assertTransitionStratumConfiguration() { - JsonRpcConfiguration jrpc = JsonRpcConfiguration.createDefault(); + final JsonRpcConfiguration jrpc = JsonRpcConfiguration.createDefault(); jrpc.setEnabled(true); - JsonRpcConfiguration engine = JsonRpcConfiguration.createEngineDefault(); + final JsonRpcConfiguration engine = JsonRpcConfiguration.createEngineDefault(); engine.setEnabled(true); - EthNetworkConfig mockMainnet = mock(EthNetworkConfig.class); + final EthNetworkConfig mockMainnet = mock(EthNetworkConfig.class); when(mockMainnet.getNetworkId()).thenReturn(BigInteger.ONE); MergeConfigOptions.setMergeEnabled(true); - MiningParameters mockMiningParams = besuController.getMiningParameters(); + final MiningParameters mockMiningParams = besuController.getMiningParameters(); when(mockMiningParams.isStratumMiningEnabled()).thenReturn(true); - TransitionCoordinator mockTransitionCoordinator = + final TransitionCoordinator mockTransitionCoordinator = spy( new TransitionCoordinator( mock(PoWMiningCoordinator.class), mock(MergeMiningCoordinator.class))); @@ -424,7 +427,6 @@ public final class RunnerBuilderTest { .vertx(Vertx.vertx()) .dataDir(dataDir.getRoot().toPath()) .storageProvider(mock(KeyValueStorageProvider.class)) - .forkIdSupplier(() -> Collections.singletonList(Bytes.EMPTY)) .rpcEndpointService(new RpcEndpointServiceImpl()) .besuPluginContext(mock(BesuPluginContextImpl.class)) .build(); diff --git a/besu/src/test/java/org/hyperledger/besu/RunnerTest.java b/besu/src/test/java/org/hyperledger/besu/RunnerTest.java index adcfb07005..8303def3b6 100644 --- a/besu/src/test/java/org/hyperledger/besu/RunnerTest.java +++ b/besu/src/test/java/org/hyperledger/besu/RunnerTest.java @@ -93,7 +93,6 @@ import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.RequestBody; import okhttp3.Response; -import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.units.bigints.UInt256; import org.awaitility.Awaitility; import org.junit.After; @@ -224,7 +223,6 @@ public final class RunnerTest { .permissioningService(new PermissioningServiceImpl()) .staticNodes(emptySet()) .storageProvider(new InMemoryKeyValueStorageProvider()) - .forkIdSupplier(() -> Collections.singletonList(Bytes.EMPTY)) .rpcEndpointService(new RpcEndpointServiceImpl()); Runner runnerBehind = null; @@ -240,7 +238,6 @@ public final class RunnerTest { .dataDir(dbAhead) .pidPath(pidPath) .besuPluginContext(new BesuPluginContextImpl()) - .forkIdSupplier(() -> controllerAhead.getProtocolManager().getForkIdAsBytesList()) .rpcEndpointService(new RpcEndpointServiceImpl()) .build(); try { @@ -295,7 +292,6 @@ public final class RunnerTest { .metricsConfiguration(behindMetricsConfiguration) .dataDir(temp.newFolder().toPath()) .metricsSystem(noOpMetricsSystem) - .forkIdSupplier(() -> controllerBehind.getProtocolManager().getForkIdAsBytesList()) .build(); runnerBehind.startExternalServices(); diff --git a/besu/src/test/java/org/hyperledger/besu/cli/CommandTestAbstract.java b/besu/src/test/java/org/hyperledger/besu/cli/CommandTestAbstract.java index 1a06abc2c4..6e473958c0 100644 --- a/besu/src/test/java/org/hyperledger/besu/cli/CommandTestAbstract.java +++ b/besu/src/test/java/org/hyperledger/besu/cli/CommandTestAbstract.java @@ -280,8 +280,8 @@ public abstract class CommandTestAbstract { when(mockRunnerBuilder.ethstatsUrl(anyString())).thenReturn(mockRunnerBuilder); when(mockRunnerBuilder.ethstatsContact(anyString())).thenReturn(mockRunnerBuilder); when(mockRunnerBuilder.storageProvider(any())).thenReturn(mockRunnerBuilder); - when(mockRunnerBuilder.forkIdSupplier(any())).thenReturn(mockRunnerBuilder); when(mockRunnerBuilder.rpcEndpointService(any())).thenReturn(mockRunnerBuilder); + when(mockRunnerBuilder.legacyForkId(anyBoolean())).thenReturn(mockRunnerBuilder); when(mockRunnerBuilder.build()).thenReturn(mockRunner); final SignatureAlgorithm signatureAlgorithm = SignatureAlgorithmFactory.getInstance(); @@ -377,11 +377,11 @@ public abstract class CommandTestAbstract { privacyPluginService); besuCommands.add(besuCommand); - File defaultKeyFile = + final File defaultKeyFile = KeyPairUtil.getDefaultKeyFile(DefaultCommandValues.getDefaultBesuDataPath(besuCommand)); try { Files.writeString(defaultKeyFile.toPath(), keyPair.getPrivateKey().toString()); - } catch (IOException e) { + } catch (final IOException e) { throw new RuntimeException(e); } besuCommand.setBesuConfiguration(commonPluginConfiguration); diff --git a/besu/src/test/java/org/hyperledger/besu/cli/options/NetworkingOptionsTest.java b/besu/src/test/java/org/hyperledger/besu/cli/options/NetworkingOptionsTest.java index 5c7c5d4a07..747b402119 100644 --- a/besu/src/test/java/org/hyperledger/besu/cli/options/NetworkingOptionsTest.java +++ b/besu/src/test/java/org/hyperledger/besu/cli/options/NetworkingOptionsTest.java @@ -152,6 +152,42 @@ public class NetworkingOptionsTest assertThat(commandOutput.toString(UTF_8)).isEmpty(); } + @Test + public void checkFilterByForkIdNotSet() { + final TestBesuCommand cmd = parseCommand(); + + final NetworkingOptions options = cmd.getNetworkingOptions(); + final NetworkingConfiguration networkingConfig = options.toDomainObject(); + assertThat(networkingConfig.getDiscovery().isFilterOnEnrForkIdEnabled()).isEqualTo(false); + + assertThat(commandErrorOutput.toString(UTF_8)).isEmpty(); + assertThat(commandOutput.toString(UTF_8)).isEmpty(); + } + + @Test + public void checkFilterByForkIdSet() { + final TestBesuCommand cmd = parseCommand(NetworkingOptions.FILTER_ON_ENR_FORK_ID + "=true"); + + final NetworkingOptions options = cmd.getNetworkingOptions(); + final NetworkingConfiguration networkingConfig = options.toDomainObject(); + assertThat(networkingConfig.getDiscovery().isFilterOnEnrForkIdEnabled()).isEqualTo(true); + + assertThat(commandErrorOutput.toString(UTF_8)).isEmpty(); + assertThat(commandOutput.toString(UTF_8)).isEmpty(); + } + + @Test + public void checkFilterByForkIdSetToFalse() { + final TestBesuCommand cmd = parseCommand(NetworkingOptions.FILTER_ON_ENR_FORK_ID + "=false"); + + final NetworkingOptions options = cmd.getNetworkingOptions(); + final NetworkingConfiguration networkingConfig = options.toDomainObject(); + assertThat(networkingConfig.getDiscovery().isFilterOnEnrForkIdEnabled()).isEqualTo(false); + + assertThat(commandErrorOutput.toString(UTF_8)).isEmpty(); + assertThat(commandOutput.toString(UTF_8)).isEmpty(); + } + @Override NetworkingConfiguration createDefaultDomainObject() { return NetworkingConfiguration.create(); diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpServiceRpcApisTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpServiceRpcApisTest.java index 9645044735..2bc7ed8d1f 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpServiceRpcApisTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpServiceRpcApisTest.java @@ -18,9 +18,11 @@ import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; import org.hyperledger.besu.config.StubGenesisConfigOptions; import org.hyperledger.besu.crypto.NodeKeyUtils; +import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.ethereum.ProtocolContext; import org.hyperledger.besu.ethereum.api.jsonrpc.health.HealthService; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.filter.FilterManager; @@ -30,6 +32,8 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.methods.JsonRpcMethodsFactory; import org.hyperledger.besu.ethereum.api.jsonrpc.websocket.WebSocketConfiguration; import org.hyperledger.besu.ethereum.api.query.BlockchainQueries; import org.hyperledger.besu.ethereum.blockcreation.PoWMiningCoordinator; +import org.hyperledger.besu.ethereum.chain.MutableBlockchain; +import org.hyperledger.besu.ethereum.core.Block; import org.hyperledger.besu.ethereum.core.InMemoryKeyValueStorageProvider; import org.hyperledger.besu.ethereum.core.PrivacyParameters; import org.hyperledger.besu.ethereum.core.ProtocolScheduleFixture; @@ -69,7 +73,6 @@ import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.RequestBody; import okhttp3.Response; -import org.apache.tuweni.bytes.Bytes; import org.junit.After; import org.junit.Before; import org.junit.Rule; @@ -261,6 +264,10 @@ public class JsonRpcHttpServiceRpcApisTest { .setRlpx(RlpxConfiguration.create().setBindPort(0)) .setDiscovery(DiscoveryConfiguration.create().setBindPort(0)); + final MutableBlockchain blockchain = mock(MutableBlockchain.class); + final Block genesisBlock = mock(Block.class); + when(blockchain.getGenesisBlock()).thenReturn(genesisBlock); + when(genesisBlock.getHash()).thenReturn(Hash.ZERO); final P2PNetwork p2pNetwork = DefaultP2PNetwork.builder() .supportedCapabilities(Capability.create("eth", 63)) @@ -269,7 +276,8 @@ public class JsonRpcHttpServiceRpcApisTest { .config(config) .metricsSystem(new NoOpMetricsSystem()) .storageProvider(new InMemoryKeyValueStorageProvider()) - .forkIdSupplier(() -> Collections.singletonList(Bytes.EMPTY)) + .blockchain(blockchain) + .forks(Collections.emptyList()) .build(); p2pNetwork.start(); @@ -412,7 +420,7 @@ public class JsonRpcHttpServiceRpcApisTest { WebSocketConfiguration webSocketConfiguration = WebSocketConfiguration.createDefault(); P2PNetwork p2pNetwork = mock(P2PNetwork.class); MetricsConfiguration metricsConfiguration = MetricsConfiguration.builder().build(); - NatService natService = mock(NatService.class); + final NatService natService = mock(NatService.class); if (enabledNetServices[netServices.indexOf("jsonrpc")]) { jsonRpcConfiguration = createJsonRpcConfiguration(); diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/ForkId.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/forkid/ForkId.java similarity index 80% rename from ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/ForkId.java rename to ethereum/core/src/main/java/org/hyperledger/besu/ethereum/forkid/ForkId.java index b854ed6220..921fdffe3f 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/ForkId.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/forkid/ForkId.java @@ -12,7 +12,7 @@ * * SPDX-License-Identifier: Apache-2.0 */ -package org.hyperledger.besu.ethereum.eth.manager; +package org.hyperledger.besu.ethereum.forkid; import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput; import org.hyperledger.besu.ethereum.rlp.RLPInput; @@ -21,6 +21,7 @@ import org.hyperledger.besu.util.EndianUtils; import java.util.ArrayList; import java.util.List; +import java.util.Optional; import org.apache.tuweni.bytes.Bytes; @@ -39,6 +40,19 @@ public class ForkId { this(hash, Bytes.wrap(EndianUtils.longToBigEndian(next)).trimLeadingZeros()); } + public static Optional fromRawForkId(final Object rawForkId) { + if (rawForkId != null) { + try { + @SuppressWarnings("unchecked") + final List> typedRawForkId = (List>) rawForkId; + return Optional.of(new ForkId(typedRawForkId.get(0).get(0), typedRawForkId.get(0).get(1))); + } catch (final Exception e) { + return Optional.empty(); + } + } + return Optional.empty(); + } + public long getNext() { return next.toLong(); } @@ -48,7 +62,7 @@ public class ForkId { } public List getForkIdAsBytesList() { - List bytesList = new ArrayList<>(); + final List bytesList = new ArrayList<>(); bytesList.add(hash); bytesList.add(next); @@ -99,6 +113,6 @@ public class ForkId { @Override public int hashCode() { - return super.hashCode(); + return 31 * this.hash.hashCode() * this.next.hashCode(); } } diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/ForkIdManager.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/forkid/ForkIdManager.java similarity index 98% rename from ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/ForkIdManager.java rename to ethereum/core/src/main/java/org/hyperledger/besu/ethereum/forkid/ForkIdManager.java index f07e4e40d1..cb81a164c3 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/ForkIdManager.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/forkid/ForkIdManager.java @@ -12,7 +12,7 @@ * * SPDX-License-Identifier: Apache-2.0 */ -package org.hyperledger.besu.ethereum.eth.manager; +package org.hyperledger.besu.ethereum.forkid; import static com.google.common.base.Preconditions.checkNotNull; @@ -78,7 +78,7 @@ public class ForkIdManager { } @VisibleForTesting - List getForkIds() { + public List getForkIds() { return this.forkIds; } @@ -96,7 +96,7 @@ public class ForkIdManager { * @param forkId to be validated. * @return boolean (peer valid (true) or invalid (false)) */ - boolean peerCheck(final ForkId forkId) { + public boolean peerCheck(final ForkId forkId) { if (forkId == null || onlyZerosForkBlocks) { return true; // Another method must be used to validate (i.e. genesis hash) } diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/EthPeers.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/EthPeers.java index 7554a6cbe5..729d2908e4 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/EthPeers.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/EthPeers.java @@ -89,16 +89,16 @@ public class EthPeers { this.maxPeers = maxPeers; this.maxMessageSize = maxMessageSize; this.bestPeerComparator = HEAVIEST_CHAIN; - metricsSystem.createIntegerGauge( - BesuMetricCategory.PEERS, - "pending_peer_requests_current", - "Number of peer requests currently pending because peers are busy", - pendingRequests::size); metricsSystem.createIntegerGauge( BesuMetricCategory.ETHEREUM, "peer_count", "The current number of peers connected", () -> (int) streamAvailablePeers().count()); + metricsSystem.createIntegerGauge( + BesuMetricCategory.PEERS, + "pending_peer_requests_current", + "Number of peer requests currently pending because peers are busy", + pendingRequests::size); } public void registerConnection( @@ -112,11 +112,8 @@ public class EthPeers { maxMessageSize, clock, permissioningProviders); - final EthPeer ethPeer = connections.putIfAbsent(peerConnection, peer); - LOG.debug( - "Adding new EthPeer {} {}", - peer.getShortNodeId(), - ethPeer == null ? "for the first time" : ""); + connections.putIfAbsent(peerConnection, peer); + LOG.debug("Adding new EthPeer {}", peer.nodeId()); } public void registerDisconnect(final PeerConnection connection) { diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/EthProtocolManager.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/EthProtocolManager.java index 12fa5f6143..6ddd2e1fcc 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/EthProtocolManager.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/EthProtocolManager.java @@ -31,6 +31,8 @@ import org.hyperledger.besu.ethereum.eth.sync.BlockBroadcaster; import org.hyperledger.besu.ethereum.eth.sync.SyncMode; import org.hyperledger.besu.ethereum.eth.sync.SynchronizerConfiguration; 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.rlpx.connections.PeerConnection; import org.hyperledger.besu.ethereum.p2p.rlpx.connections.PeerConnection.PeerNotConnected; diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/messages/StatusMessage.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/messages/StatusMessage.java index d3b7d07644..64be5b280a 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/messages/StatusMessage.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/messages/StatusMessage.java @@ -16,7 +16,7 @@ package org.hyperledger.besu.ethereum.eth.messages; import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.ethereum.core.Difficulty; -import org.hyperledger.besu.ethereum.eth.manager.ForkId; +import org.hyperledger.besu.ethereum.forkid.ForkId; import org.hyperledger.besu.ethereum.p2p.rlpx.wire.AbstractMessageData; import org.hyperledger.besu.ethereum.p2p.rlpx.wire.MessageData; import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput; diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/peervalidation/PeerValidatorRunner.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/peervalidation/PeerValidatorRunner.java index 7e8fdf3418..a2afdaf555 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/peervalidation/PeerValidatorRunner.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/peervalidation/PeerValidatorRunner.java @@ -65,7 +65,7 @@ public class PeerValidatorRunner { } protected void scheduleNextCheck(final EthPeer ethPeer) { - Duration timeout = peerValidator.nextValidationCheckTimeout(ethPeer); + final Duration timeout = peerValidator.nextValidationCheckTimeout(ethPeer); ethContext.getScheduler().scheduleFutureTask(() -> checkPeer(ethPeer), timeout); } } diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/PivotSelectorFromPeers.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/PivotSelectorFromPeers.java index f23faa824c..9999f991ec 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/PivotSelectorFromPeers.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/PivotSelectorFromPeers.java @@ -125,7 +125,7 @@ public class PivotSelectorFromPeers implements PivotBlockSelector { } private long conservativelyEstimatedPivotBlock() { - long estimatedNextPivot = + final long estimatedNextPivot = syncState.getLocalChainHeight() + syncConfig.getFastSyncPivotDistance(); return Math.min(syncState.bestChainHeight(), estimatedNextPivot); } diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/ForkIdBackwardCompatibilityTest.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/ForkIdBackwardCompatibilityTest.java index 7331c69b39..a447d28c2d 100644 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/ForkIdBackwardCompatibilityTest.java +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/ForkIdBackwardCompatibilityTest.java @@ -18,8 +18,8 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.hyperledger.besu.ethereum.eth.ForkIdTestUtil.GenesisHash; import static org.hyperledger.besu.ethereum.eth.ForkIdTestUtil.mockBlockchain; -import org.hyperledger.besu.ethereum.eth.manager.ForkId; -import org.hyperledger.besu.ethereum.eth.manager.ForkIdManager; +import org.hyperledger.besu.ethereum.forkid.ForkId; +import org.hyperledger.besu.ethereum.forkid.ForkIdManager; import java.util.Arrays; import java.util.Collection; diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/ForkIdTestUtil.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/ForkIdTestUtil.java index 83bf8b1dd0..c5e1b567c2 100644 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/ForkIdTestUtil.java +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/ForkIdTestUtil.java @@ -21,7 +21,7 @@ import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.ethereum.chain.Blockchain; import org.hyperledger.besu.ethereum.core.Block; import org.hyperledger.besu.ethereum.core.BlockHeader; -import org.hyperledger.besu.ethereum.eth.manager.ForkId; +import org.hyperledger.besu.ethereum.forkid.ForkId; import java.util.Arrays; import java.util.List; diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/LegacyForkIdManager.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/LegacyForkIdManager.java index c972ae4d8e..ec1c84dfec 100644 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/LegacyForkIdManager.java +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/LegacyForkIdManager.java @@ -16,7 +16,7 @@ package org.hyperledger.besu.ethereum.eth; import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.ethereum.chain.Blockchain; -import org.hyperledger.besu.ethereum.eth.manager.ForkId; +import org.hyperledger.besu.ethereum.forkid.ForkId; import org.hyperledger.besu.ethereum.rlp.RLPInput; import java.util.ArrayList; diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/EIP2124Test.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/EIP2124Test.java index edc6479569..a172a74ad4 100644 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/EIP2124Test.java +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/EIP2124Test.java @@ -25,6 +25,8 @@ import org.hyperledger.besu.ethereum.eth.ForkIdTestUtil.ForkIds; import org.hyperledger.besu.ethereum.eth.ForkIdTestUtil.GenesisHash; import org.hyperledger.besu.ethereum.eth.ForkIdTestUtil.Network; import org.hyperledger.besu.ethereum.eth.ForkIdTestUtil.PeerCheckCase; +import org.hyperledger.besu.ethereum.forkid.ForkId; +import org.hyperledger.besu.ethereum.forkid.ForkIdManager; import java.util.Arrays; import java.util.Collection; diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/EthProtocolManagerTest.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/EthProtocolManagerTest.java index aeabd2c2b8..4076c5c32f 100644 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/EthProtocolManagerTest.java +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/EthProtocolManagerTest.java @@ -64,6 +64,7 @@ import org.hyperledger.besu.ethereum.eth.sync.state.SyncState; import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool; import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration; import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolFactory; +import org.hyperledger.besu.ethereum.forkid.ForkIdManager; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.p2p.rlpx.connections.PeerConnection; import org.hyperledger.besu.ethereum.p2p.rlpx.wire.Capability; diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/EthProtocolManagerTestUtil.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/EthProtocolManagerTestUtil.java index 7e874ce675..de434104f6 100644 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/EthProtocolManagerTestUtil.java +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/EthProtocolManagerTestUtil.java @@ -31,6 +31,7 @@ import org.hyperledger.besu.ethereum.eth.manager.DeterministicEthScheduler.Timeo import org.hyperledger.besu.ethereum.eth.peervalidation.PeerValidator; import org.hyperledger.besu.ethereum.eth.sync.SynchronizerConfiguration; import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool; +import org.hyperledger.besu.ethereum.forkid.ForkIdManager; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.p2p.rlpx.wire.DefaultMessage; import org.hyperledger.besu.ethereum.p2p.rlpx.wire.MessageData; diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/messages/StatusMessageTest.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/messages/StatusMessageTest.java index 27e5ddf29d..2fa6acd9c2 100644 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/messages/StatusMessageTest.java +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/messages/StatusMessageTest.java @@ -19,7 +19,7 @@ import static org.assertj.core.api.Assertions.assertThat; import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.ethereum.core.Difficulty; import org.hyperledger.besu.ethereum.eth.EthProtocolVersion; -import org.hyperledger.besu.ethereum.eth.manager.ForkId; +import org.hyperledger.besu.ethereum.forkid.ForkId; import org.hyperledger.besu.ethereum.p2p.rlpx.wire.MessageData; import java.math.BigInteger; diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/TestNode.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/TestNode.java index 005cac00b6..11c005f06f 100644 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/TestNode.java +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/TestNode.java @@ -179,7 +179,8 @@ public class TestNode implements Closeable { .metricsSystem(new NoOpMetricsSystem()) .supportedCapabilities(capabilities) .storageProvider(new InMemoryKeyValueStorageProvider()) - .forkIdSupplier(() -> Collections.singletonList(Bytes.EMPTY)) + .blockchain(blockchain) + .forks(Collections.emptyList()) .build()) .metricsSystem(new NoOpMetricsSystem()) .build(); diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPoolFactoryTest.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPoolFactoryTest.java index 37ccab82c6..9c45d60e60 100644 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPoolFactoryTest.java +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPoolFactoryTest.java @@ -37,11 +37,11 @@ import org.hyperledger.besu.ethereum.eth.manager.EthMessages; 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.manager.ForkIdManager; import org.hyperledger.besu.ethereum.eth.manager.RespondingEthPeer; import org.hyperledger.besu.ethereum.eth.sync.SynchronizerConfiguration; import org.hyperledger.besu.ethereum.eth.sync.state.SyncState; import org.hyperledger.besu.ethereum.eth.transactions.sorter.GasPricePendingTransactionsSorter; +import org.hyperledger.besu.ethereum.forkid.ForkIdManager; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.p2p.rlpx.wire.messages.DisconnectMessage; import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; diff --git a/ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/config/DiscoveryConfiguration.java b/ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/config/DiscoveryConfiguration.java index 3b4bdb8f36..c2d1f19542 100644 --- a/ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/config/DiscoveryConfiguration.java +++ b/ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/config/DiscoveryConfiguration.java @@ -32,6 +32,7 @@ public class DiscoveryConfiguration { private List bootnodes = new ArrayList<>(); private String dnsDiscoveryURL; private boolean discoveryV5Enabled = false; + private boolean filterOnEnrForkId = false; public static DiscoveryConfiguration create() { return new DiscoveryConfiguration(); @@ -114,12 +115,20 @@ public class DiscoveryConfiguration { return this; } + public void setDiscoveryV5Enabled(final boolean discoveryV5Enabled) { + this.discoveryV5Enabled = discoveryV5Enabled; + } + public boolean isDiscoveryV5Enabled() { return discoveryV5Enabled; } - public void setDiscoveryV5Enabled(final boolean discoveryV5Enabled) { - this.discoveryV5Enabled = discoveryV5Enabled; + public void setFilterOnEnrForkId(final boolean filterOnEnrForkId) { + this.filterOnEnrForkId = filterOnEnrForkId; + } + + public boolean isFilterOnEnrForkIdEnabled() { + return filterOnEnrForkId; } @Override @@ -165,6 +174,10 @@ public class DiscoveryConfiguration { + bootnodes + ", dnsDiscoveryURL=" + dnsDiscoveryURL + + ", isDiscoveryV5Enabled=" + + discoveryV5Enabled + + ", isFilterOnEnrForkIdEnabled=" + + filterOnEnrForkId + '}'; } } diff --git a/ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/discovery/DiscoveryPeer.java b/ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/discovery/DiscoveryPeer.java index 75d9470fb1..f8bdd55d9c 100644 --- a/ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/discovery/DiscoveryPeer.java +++ b/ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/discovery/DiscoveryPeer.java @@ -14,6 +14,7 @@ */ package org.hyperledger.besu.ethereum.p2p.discovery; +import org.hyperledger.besu.ethereum.forkid.ForkId; import org.hyperledger.besu.ethereum.p2p.peers.DefaultPeer; import org.hyperledger.besu.ethereum.p2p.peers.Peer; import org.hyperledger.besu.ethereum.p2p.peers.PeerId; @@ -42,6 +43,7 @@ public class DiscoveryPeer extends DefaultPeer { private long lastAttemptedConnection = 0; private NodeRecord nodeRecord; + private Optional forkId = Optional.empty(); private DiscoveryPeer(final EnodeURL enode, final Endpoint endpoint) { super(enode); @@ -136,6 +138,11 @@ public class DiscoveryPeer extends DefaultPeer { public void setNodeRecord(final NodeRecord nodeRecord) { this.nodeRecord = nodeRecord; + this.forkId = ForkId.fromRawForkId(nodeRecord.get("eth")); + } + + public Optional getForkId() { + return this.forkId; } public boolean discoveryEndpointMatches(final DiscoveryPeer peer) { diff --git a/ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/discovery/PeerDiscoveryAgent.java b/ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/discovery/PeerDiscoveryAgent.java index 2db02b14ee..af39fb4957 100644 --- a/ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/discovery/PeerDiscoveryAgent.java +++ b/ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/discovery/PeerDiscoveryAgent.java @@ -21,10 +21,10 @@ import org.hyperledger.besu.crypto.Hash; import org.hyperledger.besu.crypto.NodeKey; import org.hyperledger.besu.crypto.SignatureAlgorithm; import org.hyperledger.besu.crypto.SignatureAlgorithmFactory; +import org.hyperledger.besu.ethereum.forkid.ForkIdManager; import org.hyperledger.besu.ethereum.p2p.config.DiscoveryConfiguration; import org.hyperledger.besu.ethereum.p2p.discovery.internal.Packet; import org.hyperledger.besu.ethereum.p2p.discovery.internal.PeerDiscoveryController; -import org.hyperledger.besu.ethereum.p2p.discovery.internal.PeerDiscoveryController.AsyncExecutor; import org.hyperledger.besu.ethereum.p2p.discovery.internal.PeerRequirement; import org.hyperledger.besu.ethereum.p2p.discovery.internal.PingPacketData; import org.hyperledger.besu.ethereum.p2p.discovery.internal.TimerUtil; @@ -84,6 +84,8 @@ public abstract class PeerDiscoveryAgent { private final NatService natService; private final MetricsSystem metricsSystem; private final RlpxAgent rlpxAgent; + private final ForkIdManager forkIdManager; + /* The peer controller, which takes care of the state machine of peers. */ protected Optional controller = Optional.empty(); @@ -110,7 +112,7 @@ public abstract class PeerDiscoveryAgent { final NatService natService, final MetricsSystem metricsSystem, final StorageProvider storageProvider, - final Supplier> forkIdSupplier, + final ForkIdManager forkIdManager, final RlpxAgent rlpxAgent) { this.metricsSystem = metricsSystem; checkArgument(nodeKey != null, "nodeKey cannot be null"); @@ -129,13 +131,14 @@ public abstract class PeerDiscoveryAgent { this.id = nodeKey.getPublicKey().getEncodedBytes(); this.storageProvider = storageProvider; - this.forkIdSupplier = forkIdSupplier; + this.forkIdManager = forkIdManager; + this.forkIdSupplier = () -> forkIdManager.getForkIdForChainHead().getForkIdAsBytesList(); this.rlpxAgent = rlpxAgent; } protected abstract TimerUtil createTimer(); - protected abstract AsyncExecutor createWorkerExecutor(); + protected abstract PeerDiscoveryController.AsyncExecutor createWorkerExecutor(); protected abstract CompletableFuture listenForConnections(); @@ -250,6 +253,10 @@ public abstract class PeerDiscoveryAgent { this.peerRequirements.add(peerRequirement); } + public boolean checkForkId(final DiscoveryPeer peer) { + return peer.getForkId().map(forkIdManager::peerCheck).orElse(true); + } + private void startController(final DiscoveryPeer localNode) { final PeerDiscoveryController controller = createController(localNode); this.controller = Optional.of(controller); @@ -267,6 +274,8 @@ public abstract class PeerDiscoveryAgent { .peerRequirement(PeerRequirement.combine(peerRequirements)) .peerPermissions(peerPermissions) .metricsSystem(metricsSystem) + .forkIdManager(forkIdManager) + .filterOnEnrForkId((config.isFilterOnEnrForkIdEnabled())) .rlpxAgent(rlpxAgent) .build(); } diff --git a/ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/discovery/PeerDiscoveryStatus.java b/ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/discovery/PeerDiscoveryStatus.java index 4e67aaf7a4..dc70491565 100644 --- a/ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/discovery/PeerDiscoveryStatus.java +++ b/ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/discovery/PeerDiscoveryStatus.java @@ -33,7 +33,10 @@ public enum PeerDiscoveryStatus { * We have successfully bonded with this {@link DiscoveryPeer}, and we are able to exchange * messages with them. */ - BONDED; + BONDED, + + /** We have requested the ENR record from this {@link DiscoveryPeer} */ + ENR_REQUESTED; @Override public String toString() { diff --git a/ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/discovery/VertxPeerDiscoveryAgent.java b/ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/discovery/VertxPeerDiscoveryAgent.java index 1c93d16399..4efc8df9a5 100644 --- a/ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/discovery/VertxPeerDiscoveryAgent.java +++ b/ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/discovery/VertxPeerDiscoveryAgent.java @@ -20,6 +20,7 @@ import static org.hyperledger.besu.util.Slf4jLambdaHelper.debugLambda; import static org.hyperledger.besu.util.Slf4jLambdaHelper.traceLambda; import org.hyperledger.besu.crypto.NodeKey; +import org.hyperledger.besu.ethereum.forkid.ForkIdManager; import org.hyperledger.besu.ethereum.p2p.config.DiscoveryConfiguration; import org.hyperledger.besu.ethereum.p2p.discovery.internal.Packet; import org.hyperledger.besu.ethereum.p2p.discovery.internal.PeerDiscoveryController; @@ -39,7 +40,6 @@ import java.net.BindException; import java.net.InetSocketAddress; import java.net.SocketException; import java.nio.channels.UnsupportedAddressTypeException; -import java.util.List; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.function.IntSupplier; @@ -55,7 +55,6 @@ import io.vertx.core.Vertx; import io.vertx.core.datagram.DatagramPacket; import io.vertx.core.datagram.DatagramSocket; import io.vertx.core.datagram.DatagramSocketOptions; -import org.apache.tuweni.bytes.Bytes; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -74,7 +73,7 @@ public class VertxPeerDiscoveryAgent extends PeerDiscoveryAgent { final NatService natService, final MetricsSystem metricsSystem, final StorageProvider storageProvider, - final Supplier> forkIdSupplier, + final ForkIdManager forkIdManager, final RlpxAgent rlpxAgent) { super( nodeKey, @@ -83,7 +82,7 @@ public class VertxPeerDiscoveryAgent extends PeerDiscoveryAgent { natService, metricsSystem, storageProvider, - forkIdSupplier, + forkIdManager, rlpxAgent); checkArgument(vertx != null, "vertx instance cannot be null"); this.vertx = vertx; diff --git a/ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/discovery/internal/PeerDiscoveryController.java b/ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/discovery/internal/PeerDiscoveryController.java index 13b9a6c5fe..592a5d8c83 100644 --- a/ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/discovery/internal/PeerDiscoveryController.java +++ b/ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/discovery/internal/PeerDiscoveryController.java @@ -21,6 +21,8 @@ import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.SECONDS; import org.hyperledger.besu.crypto.NodeKey; +import org.hyperledger.besu.ethereum.forkid.ForkId; +import org.hyperledger.besu.ethereum.forkid.ForkIdManager; import org.hyperledger.besu.ethereum.p2p.discovery.DiscoveryPeer; import org.hyperledger.besu.ethereum.p2p.discovery.PeerDiscoveryStatus; import org.hyperledger.besu.ethereum.p2p.peers.Peer; @@ -114,7 +116,8 @@ public class PeerDiscoveryController { private final Collection bootstrapNodes; /* A tracker for inflight interactions and the state machine of a peer. */ - private final Map inflightInteractions = new ConcurrentHashMap<>(); + private final Map> inflightInteractions = + new ConcurrentHashMap<>(); private final AtomicBoolean started = new AtomicBoolean(false); @@ -126,6 +129,8 @@ public class PeerDiscoveryController { private final DiscoveryProtocolLogger discoveryProtocolLogger; private final LabelledMetric interactionCounter; private final LabelledMetric interactionRetryCounter; + private final ForkIdManager forkIdManager; + private final boolean filterOnEnrForkId; private final RlpxAgent rlpxAgent; private RetryDelayFunction retryDelayFunction = RetryDelayFunction.linear(1.5, 2000, 60000); @@ -156,6 +161,8 @@ public class PeerDiscoveryController { final PeerPermissions peerPermissions, final MetricsSystem metricsSystem, final Optional> maybeCacheForEnrRequests, + final ForkIdManager forkIdManager, + final boolean filterOnEnrForkId, final RlpxAgent rlpxAgent) { this.timerUtil = timerUtil; this.nodeKey = nodeKey; @@ -168,7 +175,6 @@ public class PeerDiscoveryController { this.peerRequirement = peerRequirement; this.outboundMessageHandler = outboundMessageHandler; this.discoveryProtocolLogger = new DiscoveryProtocolLogger(metricsSystem); - this.peerPermissions = new PeerDiscoveryPermissions(localPeer, peerPermissions); this.rlpxAgent = rlpxAgent; @@ -178,14 +184,14 @@ public class PeerDiscoveryController { "Current number of inflight discovery interactions", inflightInteractions::size); - interactionCounter = + this.interactionCounter = metricsSystem.createLabelledCounter( BesuMetricCategory.NETWORK, "discovery_interaction_count", "Total number of discovery interactions initiated", "type"); - interactionRetryCounter = + this.interactionRetryCounter = metricsSystem.createLabelledCounter( BesuMetricCategory.NETWORK, "discovery_interaction_retry_count", @@ -194,6 +200,9 @@ public class PeerDiscoveryController { this.cachedEnrRequests = maybeCacheForEnrRequests.orElse( CacheBuilder.newBuilder().maximumSize(50).expireAfterWrite(10, SECONDS).build()); + + this.forkIdManager = forkIdManager; + this.filterOnEnrForkId = filterOnEnrForkId; } public static Builder builder() { @@ -205,6 +214,7 @@ public class PeerDiscoveryController { throw new IllegalStateException("The peer table had already been started"); } + LOG.debug("Starting with filterOnEnrForkId = {}", filterOnEnrForkId); final List initialDiscoveryPeers = bootstrapNodes.stream() .filter(peerPermissions::isAllowedInPeerTable) @@ -246,7 +256,13 @@ public class PeerDiscoveryController { tableRefreshTimerId = OptionalLong.empty(); cleanTableTimerId.ifPresent(timerUtil::cancelTimer); cleanTableTimerId = OptionalLong.empty(); - inflightInteractions.values().forEach(PeerInteractionState::cancelTimers); + inflightInteractions + .values() + .forEach( + l -> { + l.values().forEach(s -> s.cancelTimers()); + l.clear(); + }); inflightInteractions.clear(); return CompletableFuture.completedFuture(null); } @@ -314,6 +330,9 @@ public class PeerDiscoveryController { matchInteraction(packet) .ifPresent( interaction -> { + if (filterOnEnrForkId) { + requestENR(peer); + } bondingPeers.invalidate(peer.getId()); addToPeerTable(peer); recursivePeerRefreshState.onBondingComplete(peer); @@ -350,13 +369,34 @@ public class PeerDiscoveryController { } break; case ENR_RESPONSE: - // Currently there is no use case where an ENRResponse will be sent otherwise - // logic can be added here to query and store the response ENRs - packet - .getPacketData(ENRResponsePacketData.class) - .filter(p -> p.getEnr().getNodeId().equals(sender.getId())) - .ifPresent(p -> LOG.debug("Received NodeRecord: {}", p.getEnr().asEnr())); - + matchInteraction(packet) + .ifPresent( + interaction -> { + final Optional packetData = + packet.getPacketData(ENRResponsePacketData.class); + final NodeRecord enr = packetData.get().getEnr(); + peer.setNodeRecord(enr); + + final Optional maybeForkId = peer.getForkId(); + if (maybeForkId.isPresent()) { + if (forkIdManager.peerCheck(maybeForkId.get())) { + connectOnRlpxLayer(peer); + LOG.debug( + "Peer {} PASSED fork id check. ForkId received: {}", + sender.getId(), + maybeForkId.get()); + } else { + LOG.debug( + "Peer {} FAILED fork id check. ForkId received: {}", + sender.getId(), + maybeForkId.get()); + } + } else { + // if the peer hasn't sent the ForkId try to connect to it anyways + connectOnRlpxLayer(peer); + LOG.debug("No fork id sent by peer: {}", peer.getId()); + } + }); break; } } @@ -391,7 +431,9 @@ public class PeerDiscoveryController { if (peer.getStatus() != PeerDiscoveryStatus.BONDED) { peer.setStatus(PeerDiscoveryStatus.BONDED); - connectOnRlpxLayer(peer); + if (!filterOnEnrForkId) { + connectOnRlpxLayer(peer); + } } final PeerTable.AddResult result = peerTable.tryAdd(peer); @@ -408,17 +450,26 @@ public class PeerDiscoveryController { return true; } - private void connectOnRlpxLayer(final DiscoveryPeer peer) { + void connectOnRlpxLayer(final DiscoveryPeer peer) { rlpxAgent.connect(peer); } private Optional matchInteraction(final Packet packet) { - final PeerInteractionState interaction = inflightInteractions.get(packet.getNodeId()); + final Bytes nodeId = packet.getNodeId(); + final Map stateMap = inflightInteractions.get(nodeId); + if (stateMap == null) { + return Optional.empty(); + } + final PacketType packetType = packet.getType(); + final PeerInteractionState interaction = stateMap.get(packetType); if (interaction == null || !interaction.test(packet)) { return Optional.empty(); } interaction.cancelTimers(); - inflightInteractions.remove(packet.getNodeId()); + stateMap.remove(packetType); + if (stateMap.isEmpty()) { + inflightInteractions.remove(nodeId); + } return Optional.of(interaction); } @@ -498,7 +549,44 @@ public class PeerDiscoveryController { // The filter condition will be updated as soon as the action is performed. final PeerInteractionState peerInteractionState = - new PeerInteractionState(action, peer.getId(), PacketType.PONG, packet -> false, true); + new PeerInteractionState(action, peer.getId(), PacketType.PONG, packet -> false); + dispatchInteraction(peer, peerInteractionState); + } + + /** + * Initiates an enr request cycle with a peer. + * + * @param peer The targeted peer. + */ + @VisibleForTesting + void requestENR(final DiscoveryPeer peer) { + peer.setStatus(PeerDiscoveryStatus.ENR_REQUESTED); + + final Consumer action = + interaction -> { + final ENRRequestPacketData data = ENRRequestPacketData.create(); + createPacket( + PacketType.ENR_REQUEST, + data, + enrPacket -> { + final Bytes enrHash = enrPacket.getHash(); + // Update the matching filter to only accept the ENRResponse if it echoes the hash + // of our request. + final Predicate newFilter = + packet -> + packet + .getPacketData(ENRResponsePacketData.class) + .map(enr -> enr.getRequestHash().equals(enrHash)) + .orElse(false); + interaction.updateFilter(newFilter); + + sendPacket(peer, enrPacket); + }); + }; + + // The filter condition will be updated as soon as the action is performed. + final PeerInteractionState peerInteractionState = + new PeerInteractionState(action, peer.getId(), PacketType.ENR_RESPONSE, packet -> false); dispatchInteraction(peer, peerInteractionState); } @@ -544,7 +632,7 @@ public class PeerDiscoveryController { sendPacket(peer, PacketType.FIND_NEIGHBORS, data); }; final PeerInteractionState interaction = - new PeerInteractionState(action, peer.getId(), PacketType.NEIGHBORS, packet -> true, true); + new PeerInteractionState(action, peer.getId(), PacketType.NEIGHBORS, packet -> true); dispatchInteraction(peer, interaction); } @@ -558,11 +646,15 @@ public class PeerDiscoveryController { * @param state The state. */ private void dispatchInteraction(final Peer peer, final PeerInteractionState state) { - final PeerInteractionState previous = inflightInteractions.put(peer.getId(), state); + final Bytes id = peer.getId(); + final PeerInteractionState previous = + inflightInteractions + .computeIfAbsent(id, k -> new ConcurrentHashMap<>()) + .put(state.expectedType, state); if (previous != null) { previous.cancelTimers(); } - state.execute(0, 0); + state.execute(); } private void respondToPing( @@ -662,22 +754,21 @@ public class PeerDiscoveryController { private final Counter retryCounter; /** A custom filter to accept transitions out of this state. */ private Predicate filter; - /** Whether the action associated to this state is retryable or not. */ - private final boolean retryable; /** Timers associated with this entry. */ private OptionalLong timerId = OptionalLong.empty(); + private long delay = 0; + private int retryCount = 0; + PeerInteractionState( final Consumer action, final Bytes peerId, final PacketType expectedType, - final Predicate filter, - final boolean retryable) { + final Predicate filter) { this.action = action; this.peerId = peerId; this.expectedType = expectedType; this.filter = filter; - this.retryable = retryable; interactionCounter.labels(expectedType.name()).inc(); retryCounter = interactionRetryCounter.labels(expectedType.name()); } @@ -691,27 +782,27 @@ public class PeerDiscoveryController { this.filter = filter; } - /** - * Executes the action associated with this state. Sets a "boomerang" timer to itself in case - * the action is retryable. - * - * @param lastTimeout the previous timeout, or 0 if this is the first time the action is being - * executed. - */ - void execute(final long lastTimeout, final int retryCount) { + /** Executes the action associated with this state. Sets a "boomerang" timer to itself. */ + void execute() { action.accept(this); - if (retryable && retryCount < MAX_RETRIES) { - final long newTimeout = retryDelayFunction.apply(lastTimeout); + if (retryCount < MAX_RETRIES) { + this.delay = retryDelayFunction.apply(this.delay); timerId = OptionalLong.of( timerUtil.setTimer( - newTimeout, + this.delay, () -> { retryCounter.inc(); - execute(newTimeout, retryCount + 1); + retryCount++; + execute(); })); } else { - inflightInteractions.remove(peerId); + final Map peerInteractionStateMap = + inflightInteractions.get(peerId); + peerInteractionStateMap.remove(expectedType); + if (peerInteractionStateMap.isEmpty()) { + inflightInteractions.remove(peerId); + } } } @@ -741,9 +832,11 @@ public class PeerDiscoveryController { private TimerUtil timerUtil; private AsyncExecutor workerExecutor; private MetricsSystem metricsSystem; + private boolean filterOnEnrForkId; private Cache cachedEnrRequests = CacheBuilder.newBuilder().maximumSize(50).expireAfterWrite(10, SECONDS).build(); + private ForkIdManager forkIdManager; private RlpxAgent rlpxAgent; private Builder() {} @@ -769,6 +862,8 @@ public class PeerDiscoveryController { peerPermissions, metricsSystem, Optional.of(cachedEnrRequests), + forkIdManager, + filterOnEnrForkId, rlpxAgent); } @@ -778,6 +873,7 @@ public class PeerDiscoveryController { validateRequiredDependency(timerUtil, "TimerUtil"); validateRequiredDependency(workerExecutor, "AsyncExecutor"); validateRequiredDependency(metricsSystem, "MetricsSystem"); + validateRequiredDependency(forkIdManager, "ForkIdManager"); validateRequiredDependency(rlpxAgent, "RlpxAgent"); } @@ -856,6 +952,11 @@ public class PeerDiscoveryController { return this; } + public Builder filterOnEnrForkId(final boolean filterOnEnrForkId) { + this.filterOnEnrForkId = filterOnEnrForkId; + return this; + } + public Builder cacheForEnrRequests(final Cache cacheToUse) { checkNotNull(cacheToUse); this.cachedEnrRequests = cacheToUse; @@ -867,5 +968,11 @@ public class PeerDiscoveryController { this.rlpxAgent = rlpxAgent; return this; } + + public Builder forkIdManager(final ForkIdManager forkIdManager) { + checkNotNull(forkIdManager); + this.forkIdManager = forkIdManager; + return this; + } } } diff --git a/ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/network/DefaultP2PNetwork.java b/ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/network/DefaultP2PNetwork.java index f4f0712550..b5b6f553ff 100644 --- a/ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/network/DefaultP2PNetwork.java +++ b/ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/network/DefaultP2PNetwork.java @@ -18,7 +18,10 @@ import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; import org.hyperledger.besu.crypto.NodeKey; +import org.hyperledger.besu.ethereum.chain.Blockchain; +import org.hyperledger.besu.ethereum.chain.MutableBlockchain; import org.hyperledger.besu.ethereum.core.Util; +import org.hyperledger.besu.ethereum.forkid.ForkIdManager; import org.hyperledger.besu.ethereum.p2p.config.NetworkingConfiguration; import org.hyperledger.besu.ethereum.p2p.discovery.DiscoveryPeer; import org.hyperledger.besu.ethereum.p2p.discovery.PeerDiscoveryAgent; @@ -65,7 +68,6 @@ 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; @@ -140,7 +142,7 @@ public class DefaultP2PNetwork implements P2PNetwork { private final AtomicBoolean started = new AtomicBoolean(false); private final AtomicBoolean stopped = new AtomicBoolean(false); private final CountDownLatch shutdownLatch = new CountDownLatch(2); - private final Duration shutdownTimeout = Duration.ofMinutes(1); + private final Duration shutdownTimeout = Duration.ofSeconds(15); private DNSDaemon dnsDaemon; /** @@ -368,6 +370,7 @@ public class DefaultP2PNetwork implements P2PNetwork { rlpxAgent.connect( streamDiscoveredPeers() .filter(peer -> peer.getStatus() == PeerDiscoveryStatus.BONDED) + .filter(peerDiscoveryAgent::checkForkId) .sorted(Comparator.comparing(DiscoveryPeer::getLastAttemptedConnection))); } @@ -475,8 +478,10 @@ public class DefaultP2PNetwork implements P2PNetwork { private MetricsSystem metricsSystem; private StorageProvider storageProvider; - private Supplier> forkIdSupplier; private Optional p2pTLSConfiguration = Optional.empty(); + private Blockchain blockchain; + private List forks; + private boolean legacyForkIdEnabled = false; public P2PNetwork build() { validate(); @@ -518,10 +523,11 @@ public class DefaultP2PNetwork implements P2PNetwork { checkState(metricsSystem != null, "MetricsSystem must be set."); checkState(storageProvider != null, "StorageProvider must be set."); checkState(peerDiscoveryAgent != null || vertx != null, "Vertx must be set."); - checkState(forkIdSupplier != null, "ForkIdSupplier must be set."); } private PeerDiscoveryAgent createDiscoveryAgent() { + final ForkIdManager forkIdManager = + new ForkIdManager(blockchain, forks, this.legacyForkIdEnabled); return new VertxPeerDiscoveryAgent( vertx, @@ -531,7 +537,7 @@ public class DefaultP2PNetwork implements P2PNetwork { natService, metricsSystem, storageProvider, - forkIdSupplier, + forkIdManager, rlpxAgent); } @@ -625,16 +631,27 @@ public class DefaultP2PNetwork implements P2PNetwork { return this; } - public Builder forkIdSupplier(final Supplier> forkIdSupplier) { - checkNotNull(forkIdSupplier); - this.forkIdSupplier = forkIdSupplier; - return this; - } - public Builder p2pTLSConfiguration(final Optional p2pTLSConfiguration) { checkNotNull(p2pTLSConfiguration); this.p2pTLSConfiguration = p2pTLSConfiguration; return this; } + + public Builder blockchain(final MutableBlockchain blockchain) { + checkNotNull(blockchain); + this.blockchain = blockchain; + return this; + } + + public Builder forks(final List forks) { + checkNotNull(forks); + this.forks = forks; + return this; + } + + public Builder legacyForkIdEnabled(final boolean legacyForkIdEnabled) { + this.legacyForkIdEnabled = legacyForkIdEnabled; + return this; + } } } diff --git a/ethereum/p2p/src/test/java/org/hyperledger/besu/ethereum/p2p/discovery/PeerDiscoveryAgentTest.java b/ethereum/p2p/src/test/java/org/hyperledger/besu/ethereum/p2p/discovery/PeerDiscoveryAgentTest.java index 36afdd62bc..742b3ced9e 100644 --- a/ethereum/p2p/src/test/java/org/hyperledger/besu/ethereum/p2p/discovery/PeerDiscoveryAgentTest.java +++ b/ethereum/p2p/src/test/java/org/hyperledger/besu/ethereum/p2p/discovery/PeerDiscoveryAgentTest.java @@ -28,6 +28,7 @@ import org.hyperledger.besu.crypto.NodeKey; import org.hyperledger.besu.crypto.NodeKeyUtils; import org.hyperledger.besu.crypto.SignatureAlgorithm; import org.hyperledger.besu.crypto.SignatureAlgorithmFactory; +import org.hyperledger.besu.ethereum.forkid.ForkId; import org.hyperledger.besu.ethereum.p2p.discovery.PeerDiscoveryTestHelper.AgentBuilder; import org.hyperledger.besu.ethereum.p2p.discovery.internal.FindNeighborsPacketData; import org.hyperledger.besu.ethereum.p2p.discovery.internal.MockPeerDiscoveryAgent; @@ -106,12 +107,11 @@ public class PeerDiscoveryAgentTest { assertThat(nodeRecord.getSeq()).isNotNull(); assertThat(nodeRecord.get("eth")).isNotNull(); assertThat(nodeRecord.get("eth")) - .isEqualTo(Collections.singletonList(Collections.singletonList(Bytes.EMPTY))); + .isEqualTo( + Collections.singletonList(new ForkId(Bytes.EMPTY, Bytes.EMPTY).getForkIdAsBytesList())); assertThat(nodeRecord.asEnr()) .isEqualTo( - "enr:-JC4QOfroMOa1sB6ajxcBKdWn3s9S4Ojl33pbRm72S5FnCwyZfskmjkJvZznQaWNTrOHrnKxw1R9xMm9rl" - + "EGOcsOyscBg2V0aMLBgIJpZIJ2NIJpcIR_AAABiXNlY3AyNTZrMaEDymNMrg1JrLQB2KTGtv6MVbcNEV" - + "v0AHacwUAPMljNMTiDdGNwAoN1ZHCCdl8"); + "enr:-JG4QF0FFhEXDu_G-1LD5lkWh5-cbnw8vJ00NvO8vGnAf85JMwLiP-Qo49DL2xYMzX3zg_d5VXhegmoVTFJRWgZAtCYBg2V0aMPCgICCaWSCdjSCaXCEfwAAAYlzZWNwMjU2azGhA8pjTK4NSay0Adikxrb-jFW3DRFb9AB2nMFADzJYzTE4g3RjcAKDdWRwgnZf"); } @Test diff --git a/ethereum/p2p/src/test/java/org/hyperledger/besu/ethereum/p2p/discovery/PeerDiscoveryTestHelper.java b/ethereum/p2p/src/test/java/org/hyperledger/besu/ethereum/p2p/discovery/PeerDiscoveryTestHelper.java index 9d55df44a9..a23c43914f 100644 --- a/ethereum/p2p/src/test/java/org/hyperledger/besu/ethereum/p2p/discovery/PeerDiscoveryTestHelper.java +++ b/ethereum/p2p/src/test/java/org/hyperledger/besu/ethereum/p2p/discovery/PeerDiscoveryTestHelper.java @@ -17,10 +17,13 @@ package org.hyperledger.besu.ethereum.p2p.discovery; import static com.google.common.base.Preconditions.checkNotNull; import static java.util.Arrays.asList; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import org.hyperledger.besu.crypto.NodeKey; import org.hyperledger.besu.crypto.NodeKeyUtils; import org.hyperledger.besu.datatypes.Hash; +import org.hyperledger.besu.ethereum.forkid.ForkId; +import org.hyperledger.besu.ethereum.forkid.ForkIdManager; import org.hyperledger.besu.ethereum.p2p.config.DiscoveryConfiguration; import org.hyperledger.besu.ethereum.p2p.discovery.internal.MockPeerDiscoveryAgent; import org.hyperledger.besu.ethereum.p2p.discovery.internal.Packet; @@ -285,6 +288,10 @@ public class PeerDiscoveryTestHelper { config.setBindPort(port); config.setActive(active); + final ForkIdManager mockForkIdManager = mock(ForkIdManager.class); + final ForkId forkId = new ForkId(Bytes.EMPTY, Bytes.EMPTY); + when(mockForkIdManager.getForkIdForChainHead()).thenReturn(forkId); + when(mockForkIdManager.peerCheck(forkId)).thenReturn(true); final MockPeerDiscoveryAgent mockPeerDiscoveryAgent = new MockPeerDiscoveryAgent( nodeKey, @@ -292,7 +299,7 @@ public class PeerDiscoveryTestHelper { peerPermissions, agents, natService, - () -> Collections.singletonList(Bytes.EMPTY), + mockForkIdManager, mock(RlpxAgent.class)); mockPeerDiscoveryAgent.getAdvertisedPeer().ifPresent(peer -> peer.setNodeRecord(nodeRecord)); diff --git a/ethereum/p2p/src/test/java/org/hyperledger/besu/ethereum/p2p/discovery/internal/MockPeerDiscoveryAgent.java b/ethereum/p2p/src/test/java/org/hyperledger/besu/ethereum/p2p/discovery/internal/MockPeerDiscoveryAgent.java index d1e053230a..87ca46b4e0 100644 --- a/ethereum/p2p/src/test/java/org/hyperledger/besu/ethereum/p2p/discovery/internal/MockPeerDiscoveryAgent.java +++ b/ethereum/p2p/src/test/java/org/hyperledger/besu/ethereum/p2p/discovery/internal/MockPeerDiscoveryAgent.java @@ -18,10 +18,10 @@ import static org.apache.tuweni.bytes.Bytes.wrapBuffer; import org.hyperledger.besu.crypto.NodeKey; import org.hyperledger.besu.ethereum.core.InMemoryKeyValueStorageProvider; +import org.hyperledger.besu.ethereum.forkid.ForkIdManager; import org.hyperledger.besu.ethereum.p2p.config.DiscoveryConfiguration; import org.hyperledger.besu.ethereum.p2p.discovery.DiscoveryPeer; import org.hyperledger.besu.ethereum.p2p.discovery.PeerDiscoveryAgent; -import org.hyperledger.besu.ethereum.p2p.discovery.internal.PeerDiscoveryController.AsyncExecutor; import org.hyperledger.besu.ethereum.p2p.permissions.PeerPermissions; import org.hyperledger.besu.ethereum.p2p.rlpx.RlpxAgent; import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; @@ -34,7 +34,6 @@ import java.util.Deque; import java.util.List; import java.util.Map; import java.util.concurrent.CompletableFuture; -import java.util.function.Supplier; import org.apache.tuweni.bytes.Bytes; import org.slf4j.Logger; @@ -54,7 +53,7 @@ public class MockPeerDiscoveryAgent extends PeerDiscoveryAgent { final PeerPermissions peerPermissions, final Map agentNetwork, final NatService natService, - final Supplier> forkIdSupplier, + final ForkIdManager forkIdManager, final RlpxAgent rlpxAgent) { super( nodeKey, @@ -63,7 +62,7 @@ public class MockPeerDiscoveryAgent extends PeerDiscoveryAgent { natService, new NoOpMetricsSystem(), new InMemoryKeyValueStorageProvider(), - forkIdSupplier, + forkIdManager, rlpxAgent); this.agentNetwork = agentNetwork; } @@ -82,7 +81,8 @@ public class MockPeerDiscoveryAgent extends PeerDiscoveryAgent { * @return A list of packets received by this agent */ public List getIncomingPackets() { - List packets = Arrays.asList(incomingPackets.toArray(new IncomingPacket[0])); + final List packets = + Arrays.asList(incomingPackets.toArray(new IncomingPacket[0])); incomingPackets.clear(); return packets; } @@ -91,19 +91,20 @@ public class MockPeerDiscoveryAgent extends PeerDiscoveryAgent { protected CompletableFuture listenForConnections() { isRunning = true; // Skip network setup for tests - InetSocketAddress address = new InetSocketAddress(config.getBindHost(), config.getBindPort()); + final InetSocketAddress address = + new InetSocketAddress(config.getBindHost(), config.getBindPort()); return CompletableFuture.completedFuture(address); } @Override protected CompletableFuture sendOutgoingPacket( final DiscoveryPeer toPeer, final Packet packet) { - CompletableFuture result = new CompletableFuture<>(); + final CompletableFuture result = new CompletableFuture<>(); if (!this.isRunning) { result.completeExceptionally(new Exception("Attempt to send message from an inactive agent")); } - MockPeerDiscoveryAgent toAgent = agentNetwork.get(toPeer.getId()); + final MockPeerDiscoveryAgent toAgent = agentNetwork.get(toPeer.getId()); if (toAgent == null) { result.completeExceptionally( new Exception( @@ -137,7 +138,7 @@ public class MockPeerDiscoveryAgent extends PeerDiscoveryAgent { } @Override - protected AsyncExecutor createWorkerExecutor() { + protected PeerDiscoveryController.AsyncExecutor createWorkerExecutor() { return new BlockingAsyncExecutor(); } diff --git a/ethereum/p2p/src/test/java/org/hyperledger/besu/ethereum/p2p/discovery/internal/PeerDiscoveryControllerTest.java b/ethereum/p2p/src/test/java/org/hyperledger/besu/ethereum/p2p/discovery/internal/PeerDiscoveryControllerTest.java index 0dc1b03ca3..74afc00bb4 100644 --- a/ethereum/p2p/src/test/java/org/hyperledger/besu/ethereum/p2p/discovery/internal/PeerDiscoveryControllerTest.java +++ b/ethereum/p2p/src/test/java/org/hyperledger/besu/ethereum/p2p/discovery/internal/PeerDiscoveryControllerTest.java @@ -31,7 +31,12 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import org.hyperledger.besu.crypto.Hash; import org.hyperledger.besu.crypto.NodeKey; +import org.hyperledger.besu.crypto.SignatureAlgorithm; +import org.hyperledger.besu.crypto.SignatureAlgorithmFactory; +import org.hyperledger.besu.ethereum.forkid.ForkId; +import org.hyperledger.besu.ethereum.forkid.ForkIdManager; import org.hyperledger.besu.ethereum.p2p.discovery.DiscoveryPeer; import org.hyperledger.besu.ethereum.p2p.discovery.Endpoint; import org.hyperledger.besu.ethereum.p2p.discovery.PeerDiscoveryStatus; @@ -49,6 +54,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Optional; import java.util.concurrent.TimeUnit; @@ -59,14 +65,19 @@ import java.util.stream.Collectors; import com.google.common.base.Ticker; import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; +import com.google.common.net.InetAddresses; import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes32; import org.apache.tuweni.bytes.MutableBytes; import org.apache.tuweni.units.bigints.UInt256; import org.apache.tuweni.units.bigints.UInt64; import org.assertj.core.api.Assertions; +import org.ethereum.beacon.discovery.schema.EnrField; +import org.ethereum.beacon.discovery.schema.IdentitySchema; import org.ethereum.beacon.discovery.schema.IdentitySchemaInterpreter; import org.ethereum.beacon.discovery.schema.NodeRecord; +import org.ethereum.beacon.discovery.schema.NodeRecordFactory; +import org.jetbrains.annotations.NotNull; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -1468,6 +1479,175 @@ public class PeerDiscoveryControllerTest { matchPacketOfType(PacketType.ENR_RESPONSE)); } + @Test + public void shouldFiltersOnForkIdSuccess() { + final List nodeKeys = PeerDiscoveryTestHelper.generateNodeKeys(1); + final List peers = helper.createDiscoveryPeers(nodeKeys); + final ForkIdManager forkIdManager = mock(ForkIdManager.class); + final DiscoveryPeer sender = peers.get(0); + final Packet enrPacket = prepareForForkIdCheck(forkIdManager, nodeKeys, sender, true); + + when(forkIdManager.peerCheck(any(ForkId.class))).thenReturn(true); + controller.onMessage(enrPacket, sender); + + final Optional maybePeer = + controller + .streamDiscoveredPeers() + .filter(p -> p.getId().equals(sender.getId())) + .findFirst(); + + assertThat(maybePeer.isPresent()).isTrue(); + assertThat(maybePeer.get().getForkId().isPresent()).isTrue(); + verify(controller, times(1)).connectOnRlpxLayer(eq(maybePeer.get())); + } + + @Test + public void shouldFiltersOnForkIdFailure() { + final List nodeKeys = PeerDiscoveryTestHelper.generateNodeKeys(1); + final List peers = helper.createDiscoveryPeers(nodeKeys); + final ForkIdManager forkIdManager = mock(ForkIdManager.class); + final DiscoveryPeer sender = peers.get(0); + final Packet enrPacket = prepareForForkIdCheck(forkIdManager, nodeKeys, sender, true); + + when(forkIdManager.peerCheck(any(ForkId.class))).thenReturn(false); + controller.onMessage(enrPacket, sender); + + final Optional maybePeer = + controller + .streamDiscoveredPeers() + .filter(p -> p.getId().equals(sender.getId())) + .findFirst(); + + assertThat(maybePeer.isPresent()).isTrue(); + assertThat(maybePeer.get().getForkId().isPresent()).isTrue(); + verify(controller, never()).connectOnRlpxLayer(eq(maybePeer.get())); + } + + @Test + public void shouldStillCallConnectIfNoForkIdSent() { + final List nodeKeys = PeerDiscoveryTestHelper.generateNodeKeys(1); + final List peers = helper.createDiscoveryPeers(nodeKeys); + final DiscoveryPeer sender = peers.get(0); + final Packet enrPacket = + prepareForForkIdCheck(mock(ForkIdManager.class), nodeKeys, sender, false); + + controller.onMessage(enrPacket, sender); + + final Optional maybePeer = + controller + .streamDiscoveredPeers() + .filter(p -> p.getId().equals(sender.getId())) + .findFirst(); + + assertThat(maybePeer.isPresent()).isTrue(); + assertThat(maybePeer.get().getForkId().isPresent()).isFalse(); + verify(controller, times(1)).connectOnRlpxLayer(eq(maybePeer.get())); + } + + @NotNull + private Packet prepareForForkIdCheck( + final ForkIdManager forkIdManager, + final List nodeKeys, + final DiscoveryPeer sender, + final boolean sendForkId) { + final HashMap packetTypeBytesHashMap = new HashMap<>(); + final OutboundMessageHandler outboundMessageHandler = + (dp, pa) -> packetTypeBytesHashMap.put(pa.getType(), pa.getHash()); + final Cache enrs = + CacheBuilder.newBuilder() + .maximumSize(50) + .expireAfterWrite(1, TimeUnit.NANOSECONDS) + .ticker( + new Ticker() { + int tickCount = 1; + + @Override + public long read() { + return tickCount += 10; + } + }) + .build(); + controller = + getControllerBuilder() + .peers(sender) + .outboundMessageHandler(outboundMessageHandler) + .enrCache(enrs) + .filterOnForkId(true) + .forkIdManager(forkIdManager) + .build(); + + // Mock the creation of the PING packet, so that we can control the hash, which gets validated + // when receiving the PONG. + final PingPacketData mockPing = + PingPacketData.create( + Optional.ofNullable(localPeer.getEndpoint()), sender.getEndpoint(), UInt64.ONE); + final Packet mockPacket = Packet.create(PacketType.PING, mockPing, nodeKeys.get(0)); + mockPingPacketCreation(mockPacket); + + controller.start(); + + final PongPacketData pongRequestPacketData = + PongPacketData.create(localPeer.getEndpoint(), mockPacket.getHash(), UInt64.ONE); + + final Packet pongPacket = + Packet.create(PacketType.PONG, pongRequestPacketData, nodeKeys.get(0)); + + controller.onMessage(pongPacket, sender); + + final NodeRecord nodeRecord = createNodeRecord(nodeKeys.get(0), sendForkId); + + final ENRResponsePacketData enrResponsePacketData = + ENRResponsePacketData.create( + packetTypeBytesHashMap.get(PacketType.ENR_REQUEST), nodeRecord); + final Packet enrPacket = + Packet.create(PacketType.ENR_RESPONSE, enrResponsePacketData, nodeKeys.get(0)); + return enrPacket; + } + + private NodeRecord createNodeRecord(final NodeKey nodeKey, final boolean sendForkId) { + final UInt64 sequenceNumber = UInt64.ZERO.add(1); + final NodeRecordFactory nodeRecordFactory = NodeRecordFactory.DEFAULT; + final SignatureAlgorithm signatureAlgorithm = SignatureAlgorithmFactory.getInstance(); + final Bytes addressBytes = Bytes.of(InetAddresses.forString("127.0.0.1").getAddress()); + + final NodeRecord nodeRecord; + if (sendForkId) { + final Bytes forkIdHash = Bytes.fromHexString("0xfc64ec04"); + final Bytes forkIdNext = Bytes.fromHexString("118c30"); + final ArrayList forkIdBytesList = new ArrayList<>(); + forkIdBytesList.add(0, forkIdHash); + forkIdBytesList.add(1, forkIdNext); + nodeRecord = + nodeRecordFactory.createFromValues( + sequenceNumber, + new EnrField(EnrField.ID, IdentitySchema.V4), + new EnrField( + EnrField.PKEY_SECP256K1, + signatureAlgorithm.compressPublicKey(nodeKey.getPublicKey())), + new EnrField(EnrField.IP_V4, addressBytes), + new EnrField(EnrField.TCP, 7890), + new EnrField(EnrField.UDP, 4871), + new EnrField("eth", Collections.singletonList(forkIdBytesList))); + } else { + nodeRecord = + nodeRecordFactory.createFromValues( + sequenceNumber, + new EnrField(EnrField.ID, IdentitySchema.V4), + new EnrField( + EnrField.PKEY_SECP256K1, + signatureAlgorithm.compressPublicKey(nodeKey.getPublicKey())), + new EnrField(EnrField.IP_V4, addressBytes), + new EnrField(EnrField.TCP, 7890), + new EnrField(EnrField.UDP, 4871)); + } + nodeRecord.setSignature( + nodeKey + .sign(Hash.keccak256(nodeRecord.serializeNoSignature())) + .encodedBytes() + .slice(0, 64)); + return nodeRecord; + } + private static Packet mockPingPacket(final DiscoveryPeer from, final DiscoveryPeer to) { final Packet packet = mock(Packet.class); @@ -1539,6 +1719,8 @@ public class PeerDiscoveryControllerTest { private Cache enrs = CacheBuilder.newBuilder().maximumSize(50).expireAfterWrite(10, TimeUnit.SECONDS).build(); + private boolean filterOnForkId = false; + private ForkIdManager forkIdManager; public static ControllerBuilder create() { return new ControllerBuilder(); @@ -1589,6 +1771,16 @@ public class PeerDiscoveryControllerTest { return this; } + public ControllerBuilder filterOnForkId(final boolean filterOnForkId) { + this.filterOnForkId = filterOnForkId; + return this; + } + + public ControllerBuilder forkIdManager(final ForkIdManager forkIdManager) { + this.forkIdManager = forkIdManager; + return this; + } + PeerDiscoveryController build() { checkNotNull(nodeKey); if (localPeer == null) { @@ -1611,6 +1803,8 @@ public class PeerDiscoveryControllerTest { .peerPermissions(peerPermissions) .metricsSystem(new NoOpMetricsSystem()) .cacheForEnrRequests(enrs) + .forkIdManager(forkIdManager == null ? mock(ForkIdManager.class) : forkIdManager) + .filterOnEnrForkId(filterOnForkId) .rlpxAgent(mock(RlpxAgent.class)) .build()); } diff --git a/ethereum/p2p/src/test/java/org/hyperledger/besu/ethereum/p2p/discovery/internal/PeerDiscoveryTableRefreshTest.java b/ethereum/p2p/src/test/java/org/hyperledger/besu/ethereum/p2p/discovery/internal/PeerDiscoveryTableRefreshTest.java index 8dc40830ce..6f6285f9a1 100644 --- a/ethereum/p2p/src/test/java/org/hyperledger/besu/ethereum/p2p/discovery/internal/PeerDiscoveryTableRefreshTest.java +++ b/ethereum/p2p/src/test/java/org/hyperledger/besu/ethereum/p2p/discovery/internal/PeerDiscoveryTableRefreshTest.java @@ -24,6 +24,7 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import org.hyperledger.besu.crypto.NodeKey; +import org.hyperledger.besu.ethereum.forkid.ForkIdManager; import org.hyperledger.besu.ethereum.p2p.discovery.DiscoveryPeer; import org.hyperledger.besu.ethereum.p2p.discovery.PeerDiscoveryStatus; import org.hyperledger.besu.ethereum.p2p.discovery.PeerDiscoveryTestHelper; @@ -71,6 +72,7 @@ public class PeerDiscoveryTableRefreshTest { .tableRefreshIntervalMs(0) .metricsSystem(new NoOpMetricsSystem()) .rlpxAgent(mock(RlpxAgent.class)) + .forkIdManager(mock(ForkIdManager.class)) .build()); controller.start(); diff --git a/ethereum/p2p/src/test/java/org/hyperledger/besu/ethereum/p2p/network/DefaultP2PNetworkTest.java b/ethereum/p2p/src/test/java/org/hyperledger/besu/ethereum/p2p/network/DefaultP2PNetworkTest.java index 2c4fc3e26a..ebd9264135 100644 --- a/ethereum/p2p/src/test/java/org/hyperledger/besu/ethereum/p2p/network/DefaultP2PNetworkTest.java +++ b/ethereum/p2p/src/test/java/org/hyperledger/besu/ethereum/p2p/network/DefaultP2PNetworkTest.java @@ -52,14 +52,12 @@ import org.hyperledger.besu.nat.upnp.UpnpNatManager; import org.hyperledger.besu.plugin.data.EnodeURL; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.stream.Collectors; import java.util.stream.Stream; -import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes32; import org.apache.tuweni.crypto.SECP256K1; import org.assertj.core.api.Assertions; @@ -95,12 +93,11 @@ public final class DefaultP2PNetworkTest { public void before() { lenient().when(rlpxAgent.start()).thenReturn(CompletableFuture.completedFuture(30303)); lenient().when(rlpxAgent.stop()).thenReturn(CompletableFuture.completedFuture(null)); + lenient().when(discoveryAgent.stop()).thenReturn(CompletableFuture.completedFuture(null)); + lenient().when(discoveryAgent.checkForkId(any())).thenReturn(true); lenient() .when(discoveryAgent.start(anyInt())) - .thenAnswer( - invocation -> - CompletableFuture.completedFuture(invocation.getArgument(0, Integer.class))); - lenient().when(discoveryAgent.stop()).thenReturn(CompletableFuture.completedFuture(null)); + .thenReturn(CompletableFuture.completedFuture(Integer.valueOf(30301))); } @Test @@ -295,7 +292,8 @@ public final class DefaultP2PNetworkTest { network.attemptPeerConnections(); verify(rlpxAgent, times(1)).connect(peerStreamCaptor.capture()); - List capturedPeers = peerStreamCaptor.getValue().collect(Collectors.toList()); + final List capturedPeers = + peerStreamCaptor.getValue().collect(Collectors.toList()); assertThat(capturedPeers.contains(discoPeer)).isTrue(); assertThat(capturedPeers.size()).isEqualTo(1); } @@ -311,7 +309,8 @@ public final class DefaultP2PNetworkTest { network.attemptPeerConnections(); verify(rlpxAgent, times(1)).connect(peerStreamCaptor.capture()); - List capturedPeers = peerStreamCaptor.getValue().collect(Collectors.toList()); + final List capturedPeers = + peerStreamCaptor.getValue().collect(Collectors.toList()); assertThat(capturedPeers.contains(discoPeer)).isFalse(); assertThat(capturedPeers.size()).isEqualTo(0); } @@ -331,7 +330,8 @@ public final class DefaultP2PNetworkTest { network.attemptPeerConnections(); verify(rlpxAgent, times(1)).connect(peerStreamCaptor.capture()); - List capturedPeers = peerStreamCaptor.getValue().collect(Collectors.toList()); + final List capturedPeers = + peerStreamCaptor.getValue().collect(Collectors.toList()); assertThat(capturedPeers.size()).isEqualTo(3); assertThat(capturedPeers.get(0)).isEqualTo(discoPeers.get(1)); assertThat(capturedPeers.get(1)).isEqualTo(discoPeers.get(0)); @@ -349,7 +349,7 @@ public final class DefaultP2PNetworkTest { @Test public void shouldNotStartDnsDiscoveryWhenDnsURLIsNotConfigured() { - DefaultP2PNetwork testClass = network(); + final DefaultP2PNetwork testClass = network(); testClass.start(); // ensure DnsDaemon is NOT present: assertThat(testClass.getDnsDaemon()).isNotPresent(); @@ -358,15 +358,15 @@ public final class DefaultP2PNetworkTest { @Test public void shouldStartDnsDiscoveryWhenDnsURLIsConfigured() { // create a discovery config with a dns config - DiscoveryConfiguration disco = + final DiscoveryConfiguration disco = DiscoveryConfiguration.create().setDnsDiscoveryURL("enrtree://mock@localhost"); // spy on config to return dns discovery config: - NetworkingConfiguration dnsConfig = + final NetworkingConfiguration dnsConfig = when(spy(config).getDiscovery()).thenReturn(disco).getMock(); // spy on DefaultP2PNetwork - DefaultP2PNetwork testClass = (DefaultP2PNetwork) builder().config(dnsConfig).build(); + final DefaultP2PNetwork testClass = (DefaultP2PNetwork) builder().config(dnsConfig).build(); testClass.start(); assertThat(testClass.getDnsDaemon()).isPresent(); @@ -375,15 +375,15 @@ public final class DefaultP2PNetworkTest { @Test public void shouldUseDnsServerOverrideIfPresent() { // create a discovery config with a dns config - DiscoveryConfiguration disco = + final DiscoveryConfiguration disco = DiscoveryConfiguration.create().setDnsDiscoveryURL("enrtree://mock@localhost"); // spy on config to return dns discovery config: - NetworkingConfiguration dnsConfig = spy(config); + final NetworkingConfiguration dnsConfig = spy(config); doReturn(disco).when(dnsConfig).getDiscovery(); doReturn(Optional.of("localhost")).when(dnsConfig).getDnsDiscoveryServerOverride(); - DefaultP2PNetwork testClass = (DefaultP2PNetwork) builder().config(dnsConfig).build(); + final DefaultP2PNetwork testClass = (DefaultP2PNetwork) builder().config(dnsConfig).build(); testClass.start(); // ensure we used the dns server override config when building DNSDaemon: @@ -407,7 +407,6 @@ public final class DefaultP2PNetworkTest { .maintainedPeers(maintainedPeers) .metricsSystem(new NoOpMetricsSystem()) .supportedCapabilities(Capability.create("eth", 63)) - .storageProvider(new InMemoryKeyValueStorageProvider()) - .forkIdSupplier(() -> Collections.singletonList(Bytes.EMPTY)); + .storageProvider(new InMemoryKeyValueStorageProvider()); } } diff --git a/ethereum/p2p/src/test/java/org/hyperledger/besu/ethereum/p2p/network/NetworkingServiceLifecycleTest.java b/ethereum/p2p/src/test/java/org/hyperledger/besu/ethereum/p2p/network/NetworkingServiceLifecycleTest.java index 96340b7705..cb4aacfc3f 100644 --- a/ethereum/p2p/src/test/java/org/hyperledger/besu/ethereum/p2p/network/NetworkingServiceLifecycleTest.java +++ b/ethereum/p2p/src/test/java/org/hyperledger/besu/ethereum/p2p/network/NetworkingServiceLifecycleTest.java @@ -20,9 +20,14 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.hamcrest.Matchers.startsWith; import static org.hyperledger.besu.ethereum.p2p.NetworkingTestHelper.configWithRandomPorts; import static org.junit.Assume.assumeThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import org.hyperledger.besu.crypto.NodeKey; import org.hyperledger.besu.crypto.NodeKeyUtils; +import org.hyperledger.besu.datatypes.Hash; +import org.hyperledger.besu.ethereum.chain.MutableBlockchain; +import org.hyperledger.besu.ethereum.core.Block; import org.hyperledger.besu.ethereum.core.InMemoryKeyValueStorageProvider; import org.hyperledger.besu.ethereum.p2p.config.DiscoveryConfiguration; import org.hyperledger.besu.ethereum.p2p.config.NetworkingConfiguration; @@ -36,8 +41,8 @@ import java.util.Arrays; import java.util.Collections; import io.vertx.core.Vertx; -import org.apache.tuweni.bytes.Bytes; import org.assertj.core.api.Assertions; +import org.jetbrains.annotations.NotNull; import org.junit.After; import org.junit.Test; @@ -55,7 +60,8 @@ public class NetworkingServiceLifecycleTest { @Test public void createP2PNetwork() throws IOException { final NetworkingConfiguration config = configWithRandomPorts(); - try (final P2PNetwork service = builder().build()) { + final DefaultP2PNetwork.Builder builder = getP2PNetworkBuilder(); + try (final P2PNetwork service = builder.build()) { service.start(); final EnodeURL enode = service.getLocalEnode().get(); final int udpPort = enode.getDiscoveryPortOrZero(); @@ -68,12 +74,24 @@ public class NetworkingServiceLifecycleTest { } } + @NotNull + private DefaultP2PNetwork.Builder getP2PNetworkBuilder() { + final DefaultP2PNetwork.Builder builder = builder(); + final MutableBlockchain blockchainMock = mock(MutableBlockchain.class); + final Block blockMock = mock(Block.class); + when(blockMock.getHash()).thenReturn(Hash.ZERO); + when(blockchainMock.getGenesisBlock()).thenReturn(blockMock); + builder.blockchain(blockchainMock); + builder.forks(Collections.emptyList()); + return builder; + } + @Test public void createP2PNetwork_NullHost() throws IOException { final NetworkingConfiguration config = NetworkingConfiguration.create() .setDiscovery(DiscoveryConfiguration.create().setBindHost(null)); - final DefaultP2PNetwork.Builder p2pNetworkBuilder = builder().config(config); + final DefaultP2PNetwork.Builder p2pNetworkBuilder = getP2PNetworkBuilder().config(config); assertThatThrownBy( () -> { try (final P2PNetwork ignored = p2pNetworkBuilder.build()) { @@ -88,7 +106,7 @@ public class NetworkingServiceLifecycleTest { final NetworkingConfiguration config = NetworkingConfiguration.create() .setDiscovery(DiscoveryConfiguration.create().setBindHost("fake.fake.fake")); - final DefaultP2PNetwork.Builder p2pNetworkBuilder = builder().config(config); + final DefaultP2PNetwork.Builder p2pNetworkBuilder = getP2PNetworkBuilder().config(config); assertThatThrownBy( () -> { try (final P2PNetwork ignored = p2pNetworkBuilder.build()) { @@ -103,7 +121,7 @@ public class NetworkingServiceLifecycleTest { final NetworkingConfiguration config = NetworkingConfiguration.create() .setDiscovery(DiscoveryConfiguration.create().setBindPort(-1)); - final DefaultP2PNetwork.Builder p2pNetworkBuilder = builder().config(config); + final DefaultP2PNetwork.Builder p2pNetworkBuilder = getP2PNetworkBuilder().config(config); assertThatThrownBy( () -> { try (final P2PNetwork ignored = p2pNetworkBuilder.build()) { @@ -122,7 +140,7 @@ public class NetworkingServiceLifecycleTest { @Test public void startStopP2PNetwork() throws IOException { - try (final P2PNetwork service = builder().build()) { + try (final P2PNetwork service = getP2PNetworkBuilder().build()) { service.start(); service.stop(); } @@ -130,8 +148,8 @@ public class NetworkingServiceLifecycleTest { @Test public void startDiscoveryAgentBackToBack() throws IOException { - try (final P2PNetwork service1 = builder().build(); - final P2PNetwork service2 = builder().build()) { + try (final P2PNetwork service1 = getP2PNetworkBuilder().build(); + final P2PNetwork service2 = getP2PNetworkBuilder().build()) { service1.start(); service1.stop(); service2.start(); @@ -145,13 +163,13 @@ public class NetworkingServiceLifecycleTest { "Ignored if system language is not English", System.getProperty("user.language"), startsWith("en")); - try (final P2PNetwork service1 = builder().config(config).build()) { + try (final P2PNetwork service1 = getP2PNetworkBuilder().config(config).build()) { service1.start(); final NetworkingConfiguration config = configWithRandomPorts(); final int usedPort = service1.getLocalEnode().get().getDiscoveryPortOrZero(); assertThat(usedPort).isNotZero(); config.getDiscovery().setBindPort(usedPort); - try (final P2PNetwork service2 = builder().config(config).build()) { + try (final P2PNetwork service2 = getP2PNetworkBuilder().config(config).build()) { try { service2.start(); } catch (final Exception e) { @@ -171,7 +189,7 @@ public class NetworkingServiceLifecycleTest { @Test public void createP2PNetwork_NoActivePeers() throws IOException { - try (final P2PNetwork agent = builder().build()) { + try (final P2PNetwork agent = getP2PNetworkBuilder().build()) { assertThat(agent.streamDiscoveredPeers().collect(toList())).isEmpty(); assertThat(agent.getPeers()).isEmpty(); } @@ -184,7 +202,6 @@ public class NetworkingServiceLifecycleTest { .config(config) .metricsSystem(new NoOpMetricsSystem()) .supportedCapabilities(Arrays.asList(Capability.create("eth", 63))) - .storageProvider(new InMemoryKeyValueStorageProvider()) - .forkIdSupplier(() -> Collections.singletonList(Bytes.EMPTY)); + .storageProvider(new InMemoryKeyValueStorageProvider()); } } diff --git a/ethereum/p2p/src/test/java/org/hyperledger/besu/ethereum/p2p/network/P2PNetworkTest.java b/ethereum/p2p/src/test/java/org/hyperledger/besu/ethereum/p2p/network/P2PNetworkTest.java index 6d21ac6256..a528be6d13 100644 --- a/ethereum/p2p/src/test/java/org/hyperledger/besu/ethereum/p2p/network/P2PNetworkTest.java +++ b/ethereum/p2p/src/test/java/org/hyperledger/besu/ethereum/p2p/network/P2PNetworkTest.java @@ -23,6 +23,9 @@ import static org.mockito.Mockito.when; import org.hyperledger.besu.crypto.NodeKey; import org.hyperledger.besu.crypto.NodeKeyUtils; +import org.hyperledger.besu.datatypes.Hash; +import org.hyperledger.besu.ethereum.chain.MutableBlockchain; +import org.hyperledger.besu.ethereum.core.Block; import org.hyperledger.besu.ethereum.core.InMemoryKeyValueStorageProvider; import org.hyperledger.besu.ethereum.p2p.config.DiscoveryConfiguration; import org.hyperledger.besu.ethereum.p2p.config.NetworkingConfiguration; @@ -307,6 +310,10 @@ public class P2PNetworkTest { } private DefaultP2PNetwork.Builder builder() { + final MutableBlockchain blockchainMock = mock(MutableBlockchain.class); + final Block blockMock = mock(Block.class); + when(blockMock.getHash()).thenReturn(Hash.ZERO); + when(blockchainMock.getGenesisBlock()).thenReturn(blockMock); return DefaultP2PNetwork.builder() .vertx(vertx) .config(config) @@ -314,6 +321,7 @@ public class P2PNetworkTest { .metricsSystem(new NoOpMetricsSystem()) .supportedCapabilities(Arrays.asList(Capability.create("eth", 63))) .storageProvider(new InMemoryKeyValueStorageProvider()) - .forkIdSupplier(() -> Collections.singletonList(Bytes.EMPTY)); + .forks(Collections.emptyList()) + .blockchain(blockchainMock); } } diff --git a/ethereum/p2p/src/test/java/org/hyperledger/besu/ethereum/p2p/network/P2PPlainNetworkTest.java b/ethereum/p2p/src/test/java/org/hyperledger/besu/ethereum/p2p/network/P2PPlainNetworkTest.java index 5212d3a0c2..bb6ff961b9 100644 --- a/ethereum/p2p/src/test/java/org/hyperledger/besu/ethereum/p2p/network/P2PPlainNetworkTest.java +++ b/ethereum/p2p/src/test/java/org/hyperledger/besu/ethereum/p2p/network/P2PPlainNetworkTest.java @@ -24,6 +24,9 @@ import static org.mockito.Mockito.when; import org.hyperledger.besu.crypto.NodeKey; import org.hyperledger.besu.crypto.NodeKeyUtils; +import org.hyperledger.besu.datatypes.Hash; +import org.hyperledger.besu.ethereum.chain.MutableBlockchain; +import org.hyperledger.besu.ethereum.core.Block; import org.hyperledger.besu.ethereum.core.InMemoryKeyValueStorageProvider; import org.hyperledger.besu.ethereum.p2p.config.DiscoveryConfiguration; import org.hyperledger.besu.ethereum.p2p.config.NetworkingConfiguration; @@ -391,7 +394,7 @@ public class P2PPlainNetworkTest { } private byte[] buildPaddedMessage(final int messageSize) { - byte[] bytes = new byte[messageSize]; + final byte[] bytes = new byte[messageSize]; Arrays.fill(bytes, (byte) 9); return bytes; } @@ -416,7 +419,7 @@ public class P2PPlainNetworkTest { final Path targetFilePath = createTemporaryFile("nsscfg"); Files.write(targetFilePath, updated.getBytes(Charsets.UTF_8)); ret = targetFilePath; - } catch (IOException e) { + } catch (final IOException e) { throw new RuntimeException("Error populating nss config file", e); } return ret; @@ -427,7 +430,7 @@ public class P2PPlainNetworkTest { try { tempFile = File.createTempFile("temp", suffix); tempFile.deleteOnExit(); - } catch (IOException e) { + } catch (final IOException e) { throw new RuntimeException("Error creating temporary file", e); } return tempFile.toPath(); @@ -482,13 +485,17 @@ public class P2PPlainNetworkTest { .withCrlPath(toPath(String.format(crl, name))); break; } - } catch (Exception e) { + } catch (final Exception e) { throw new RuntimeException(e); } return Optional.of(builder.build()); } private DefaultP2PNetwork.Builder builder(final String name) { + final MutableBlockchain blockchainMock = mock(MutableBlockchain.class); + final Block blockMock = mock(Block.class); + when(blockMock.getHash()).thenReturn(Hash.ZERO); + when(blockchainMock.getGenesisBlock()).thenReturn(blockMock); return DefaultP2PNetwork.builder() .vertx(vertx) .config(config) @@ -497,6 +504,7 @@ public class P2PPlainNetworkTest { .metricsSystem(new NoOpMetricsSystem()) .supportedCapabilities(Arrays.asList(Capability.create("eth", 63))) .storageProvider(new InMemoryKeyValueStorageProvider()) - .forkIdSupplier(() -> Collections.singletonList(Bytes.EMPTY)); + .forks(Collections.emptyList()) + .blockchain(blockchainMock); } }