Ethstat retry fix (#5301)

* Fix ethstats retry logic. Attempt to connect with and without ssl. If --ethstats is specified without a port, use port 443 (for wss://) and port 80 (for ws://) as defaults instead of 3000.
* Introduced optional --ethstats-cacert-file to specify root CA for ethstats server.


Signed-off-by: Usman Saleem <usman@usmans.info>
pull/5313/head
Usman Saleem 2 years ago committed by GitHub
parent a55e824eaa
commit b5a5bded90
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      CHANGELOG.md
  2. 39
      besu/src/main/java/org/hyperledger/besu/RunnerBuilder.java
  3. 3
      besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java
  4. 37
      besu/src/main/java/org/hyperledger/besu/cli/options/stable/EthstatsOptions.java
  5. 6
      besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java
  6. 6
      besu/src/test/java/org/hyperledger/besu/cli/CommandTestAbstract.java
  7. 1
      besu/src/test/resources/everything_config.toml
  8. 86
      ethereum/ethstats/src/main/java/org/hyperledger/besu/ethstats/EthStatsService.java
  9. 17
      ethereum/ethstats/src/main/java/org/hyperledger/besu/ethstats/util/EthStatsConnectOptions.java
  10. 16
      ethereum/ethstats/src/test/java/org/hyperledger/besu/ethstats/EthStatsServiceTest.java
  11. 110
      ethereum/ethstats/src/test/java/org/hyperledger/besu/ethstats/util/EthStatsConnectOptionsTest.java
  12. 100
      ethereum/ethstats/src/test/java/org/hyperledger/besu/ethstats/util/NetstatsUrlTest.java

@ -12,6 +12,7 @@
### Breaking Changes
- In `evmtool` (an offline EVM executor tool principally used for reference tests), the `--prestate` and `--genesis` options no longer parse genesis files containing IBFT, QBFT, and Clique network definitions. The same genesis files will work with those json entries removed. [#5192](https://github.com/hyperledger/besu/pull/5192)
- In `--ethstats`, if the port is not specified in the URI, it will default to 443 and 80 for ssl and non-ssl connections respectively instead of 3000. [#5301](https://github.com/hyperledger/besu/pull/5301)
### Additions and Improvements
@ -19,11 +20,13 @@
- To generate the binary install and use GraalVM 23.3.r17 or higher and run `./gradlew naticeCompile`. The binary will be located in `ethereum/evmtool/build/native/nativeCompile`
- Upgrade RocksDB version from 7.7.3 to 8.0.0. Besu Team [contributed](https://github.com/facebook/rocksdb/pull/11099) to this release to make disabling checksum verification work.
- Log an error with stacktrace when RPC responds with internal error [#5288](https://github.com/hyperledger/besu/pull/5288)
- `--ethstats-cacert` to specify root CA of ethstats server (useful for non-production environments). [#5301](https://github.com/hyperledger/besu/pull/5301)
### Bug Fixes
- Fix eth_getBlockByNumber cache error for latest block when called during syncing [#5292](https://github.com/hyperledger/besu/pull/5292)
- Fix QBFT and IBFT unable to propose blocks on London when zeroBaseFee is used [#5276](https://github.com/hyperledger/besu/pull/5276)
- Make QBFT validator smart contract mode work with london fork [#5249](https://github.com/hyperledger/besu/issues/5249)
- Try to connect to EthStats server by default with ssl followed by non-ssl. [#5301](https://github.com/hyperledger/besu/pull/5301)
- Allow --miner-extra-data to be used in Proof-of-Stake block production [#5291](https://github.com/hyperledger/besu/pull/5291)
### Download Links

@ -23,6 +23,7 @@ import static org.hyperledger.besu.ethereum.core.PrivacyParameters.FLEXIBLE_PRIV
import org.hyperledger.besu.cli.config.EthNetworkConfig;
import org.hyperledger.besu.cli.config.NetworkName;
import org.hyperledger.besu.cli.options.stable.EthstatsOptions;
import org.hyperledger.besu.consensus.merge.blockcreation.TransitionCoordinator;
import org.hyperledger.besu.controller.BesuController;
import org.hyperledger.besu.cryptoservices.NodeKey;
@ -105,7 +106,7 @@ import org.hyperledger.besu.ethereum.stratum.StratumServer;
import org.hyperledger.besu.ethereum.transaction.TransactionSimulator;
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
import org.hyperledger.besu.ethstats.EthStatsService;
import org.hyperledger.besu.ethstats.util.NetstatsUrl;
import org.hyperledger.besu.ethstats.util.EthStatsConnectOptions;
import org.hyperledger.besu.metrics.MetricsService;
import org.hyperledger.besu.metrics.ObservableMetricsSystem;
import org.hyperledger.besu.metrics.prometheus.MetricsConfiguration;
@ -172,9 +173,7 @@ public class RunnerBuilder {
private boolean limitRemoteWireConnectionsEnabled = false;
private float fractionRemoteConnectionsAllowed;
private EthNetworkConfig ethNetworkConfig;
private String ethstatsUrl;
private String ethstatsContact;
private EthstatsOptions ethstatsOptions;
private JsonRpcConfiguration jsonRpcConfiguration;
private Optional<JsonRpcConfiguration> engineJsonRpcConfiguration = Optional.empty();
private GraphQLConfiguration graphQLConfiguration;
@ -402,24 +401,13 @@ public class RunnerBuilder {
}
/**
* Add Ethstats url.
* Add EthStatsOptions
*
* @param ethstatsUrl the ethstats url
* @return the runner builder
* @param ethstatsOptions the ethstats options
* @return Runner builder instance
*/
public RunnerBuilder ethstatsUrl(final String ethstatsUrl) {
this.ethstatsUrl = ethstatsUrl;
return this;
}
/**
* Add Ethstats contact.
*
* @param ethstatsContact the ethstats contact
* @return the runner builder
*/
public RunnerBuilder ethstatsContact(final String ethstatsContact) {
this.ethstatsContact = ethstatsContact;
public RunnerBuilder ethstatsOptions(final EthstatsOptions ethstatsOptions) {
this.ethstatsOptions = ethstatsOptions;
return this;
}
@ -1038,11 +1026,14 @@ public class RunnerBuilder {
createMetricsService(vertx, metricsConfiguration);
final Optional<EthStatsService> ethStatsService;
if (!Strings.isNullOrEmpty(ethstatsUrl)) {
if (isEthStatsEnabled()) {
ethStatsService =
Optional.of(
new EthStatsService(
NetstatsUrl.fromParams(ethstatsUrl, ethstatsContact),
EthStatsConnectOptions.fromParams(
ethstatsOptions.getEthstatsUrl(),
ethstatsOptions.getEthstatsContact(),
ethstatsOptions.getEthstatsCaCert()),
blockchainQueries,
besuController.getProtocolManager(),
transactionPool,
@ -1114,6 +1105,10 @@ public class RunnerBuilder {
context.getBlockchain());
}
private boolean isEthStatsEnabled() {
return ethstatsOptions != null && !Strings.isNullOrEmpty(ethstatsOptions.getEthstatsUrl());
}
private Stream<EnodeURL> sanitizePeers(
final P2PNetwork network, final Collection<EnodeURL> enodeURLS) {
if (network.getLocalEnode().isEmpty()) {

@ -3084,8 +3084,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
.identityString(identityString)
.besuPluginContext(besuPluginContext)
.autoLogBloomCaching(autoLogBloomCachingEnabled)
.ethstatsUrl(ethstatsOptions.getEthstatsUrl())
.ethstatsContact(ethstatsOptions.getEthstatsContact())
.ethstatsOptions(ethstatsOptions)
.storageProvider(keyValueStorageProvider(keyValueStorageName))
.rpcEndpointService(rpcEndpointServiceImpl)
.rpcMaxLogsRange(rpcMaxLogsRange)

@ -15,18 +15,20 @@
package org.hyperledger.besu.cli.options.stable;
import org.hyperledger.besu.cli.options.CLIOptions;
import org.hyperledger.besu.ethstats.util.NetstatsUrl;
import org.hyperledger.besu.ethstats.util.EthStatsConnectOptions;
import java.util.Arrays;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import picocli.CommandLine;
/** The Ethstats CLI options. */
public class EthstatsOptions implements CLIOptions<NetstatsUrl> {
public class EthstatsOptions implements CLIOptions<EthStatsConnectOptions> {
private static final String ETHSTATS = "--ethstats";
private static final String ETHSTATS_CONTACT = "--ethstats-contact";
private static final String ETHSTATS_CACERT_FILE = "--ethstats-cacert-file";
@SuppressWarnings({"FieldCanBeFinal", "FieldMayBeFinal"})
@CommandLine.Option(
@ -43,6 +45,14 @@ public class EthstatsOptions implements CLIOptions<NetstatsUrl> {
arity = "1")
private String ethstatsContact = "";
@SuppressWarnings({"FieldCanBeFinal", "FieldMayBeFinal"})
@CommandLine.Option(
names = {ETHSTATS_CACERT_FILE},
paramLabel = "<FILE>",
description =
"Specifies the path to the root CA (Certificate Authority) certificate file that has signed ethstats server certificate. This option is optional.")
private Path ethstatsCaCert = null;
private EthstatsOptions() {}
/**
@ -55,8 +65,8 @@ public class EthstatsOptions implements CLIOptions<NetstatsUrl> {
}
@Override
public NetstatsUrl toDomainObject() {
return NetstatsUrl.fromParams(ethstatsUrl, ethstatsContact);
public EthStatsConnectOptions toDomainObject() {
return EthStatsConnectOptions.fromParams(ethstatsUrl, ethstatsContact, ethstatsCaCert);
}
/**
@ -77,8 +87,23 @@ public class EthstatsOptions implements CLIOptions<NetstatsUrl> {
return ethstatsContact;
}
/**
* Returns path to root CA cert file.
*
* @return Path to CA file. null if no CA file to set.
*/
public Path getEthstatsCaCert() {
return ethstatsCaCert;
}
@Override
public List<String> getCLIOptions() {
return Arrays.asList(ETHSTATS + "=" + ethstatsUrl, ETHSTATS_CONTACT + "=" + ethstatsContact);
final List<String> options = new ArrayList<>();
options.add(ETHSTATS + "=" + ethstatsUrl);
options.add(ETHSTATS_CONTACT + "=" + ethstatsContact);
if (ethstatsCaCert != null) {
options.add(ETHSTATS_CACERT_FILE + "=" + ethstatsCaCert);
}
return options;
}
}

@ -1855,14 +1855,16 @@ public class BesuCommandTest extends CommandTestAbstract {
public void ethStatsOptionIsParsedCorrectly() {
final String url = "besu-node:secret@host:443";
parseCommand("--ethstats", url);
verify(mockRunnerBuilder).ethstatsUrl(url);
verify(mockRunnerBuilder).ethstatsOptions(ethstatsOptionsArgumentCaptor.capture());
assertThat(ethstatsOptionsArgumentCaptor.getValue().getEthstatsUrl()).isEqualTo(url);
}
@Test
public void ethStatsContactOptionIsParsedCorrectly() {
final String contact = "contact@mail.net";
parseCommand("--ethstats", "besu-node:secret@host:443", "--ethstats-contact", contact);
verify(mockRunnerBuilder).ethstatsContact(contact);
verify(mockRunnerBuilder).ethstatsOptions(ethstatsOptionsArgumentCaptor.capture());
assertThat(ethstatsOptionsArgumentCaptor.getValue().getEthstatsContact()).isEqualTo(contact);
}
@Test

@ -32,6 +32,7 @@ import org.hyperledger.besu.chainexport.RlpBlockExporter;
import org.hyperledger.besu.chainimport.JsonBlockImporter;
import org.hyperledger.besu.chainimport.RlpBlockImporter;
import org.hyperledger.besu.cli.config.EthNetworkConfig;
import org.hyperledger.besu.cli.options.stable.EthstatsOptions;
import org.hyperledger.besu.cli.options.unstable.EthProtocolOptions;
import org.hyperledger.besu.cli.options.unstable.LauncherOptions;
import org.hyperledger.besu.cli.options.unstable.MetricsCLIOptions;
@ -193,6 +194,8 @@ public abstract class CommandTestAbstract {
@Captor protected ArgumentCaptor<TransactionPoolConfiguration> transactionPoolConfigCaptor;
@Captor protected ArgumentCaptor<EthstatsOptions> ethstatsOptionsArgumentCaptor;
@Rule public final TemporaryFolder temp = new TemporaryFolder();
@Before
@ -279,8 +282,7 @@ public abstract class CommandTestAbstract {
when(mockRunnerBuilder.besuPluginContext(any())).thenReturn(mockRunnerBuilder);
when(mockRunnerBuilder.autoLogBloomCaching(anyBoolean())).thenReturn(mockRunnerBuilder);
when(mockRunnerBuilder.pidPath(any())).thenReturn(mockRunnerBuilder);
when(mockRunnerBuilder.ethstatsUrl(anyString())).thenReturn(mockRunnerBuilder);
when(mockRunnerBuilder.ethstatsContact(anyString())).thenReturn(mockRunnerBuilder);
when(mockRunnerBuilder.ethstatsOptions(any())).thenReturn(mockRunnerBuilder);
when(mockRunnerBuilder.storageProvider(any())).thenReturn(mockRunnerBuilder);
when(mockRunnerBuilder.rpcEndpointService(any())).thenReturn(mockRunnerBuilder);
when(mockRunnerBuilder.legacyForkId(anyBoolean())).thenReturn(mockRunnerBuilder);

@ -191,6 +191,7 @@ auto-log-bloom-caching-enabled=true
# ethstats
ethstats="nodename:secret@host:1234"
ethstats-contact="contact@mail.n"
ethstats-cacert-file="./root.cert"
# Data storage
data-storage-format="BONSAI"

@ -52,7 +52,7 @@ import org.hyperledger.besu.ethstats.report.ImmutablePingReport;
import org.hyperledger.besu.ethstats.report.NodeStatsReport;
import org.hyperledger.besu.ethstats.report.PendingTransactionsReport;
import org.hyperledger.besu.ethstats.request.EthStatsRequest;
import org.hyperledger.besu.ethstats.util.NetstatsUrl;
import org.hyperledger.besu.ethstats.util.EthStatsConnectOptions;
import org.hyperledger.besu.ethstats.util.PrimusHeartBeatsHelper;
import org.hyperledger.besu.plugin.data.EnodeURL;
import org.hyperledger.besu.util.platform.PlatformDetector;
@ -69,13 +69,13 @@ import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.LongStream;
import javax.net.ssl.SSLHandshakeException;
import com.fasterxml.jackson.databind.JsonNode;
import io.vertx.core.Vertx;
import io.vertx.core.http.HttpClientOptions;
import io.vertx.core.http.WebSocket;
import io.vertx.core.http.WebSocketConnectOptions;
import io.vertx.core.net.PemTrustOptions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -92,7 +92,7 @@ public class EthStatsService {
private final AtomicBoolean retryInProgress = new AtomicBoolean(false);
private final NetstatsUrl netstatsUrl;
private final EthStatsConnectOptions ethStatsConnectOptions;
private final EthProtocolManager protocolManager;
private final TransactionPool transactionPool;
private final MiningCoordinator miningCoordinator;
@ -114,7 +114,7 @@ public class EthStatsService {
/**
* Instantiates a new EthStats service.
*
* @param netstatsUrl the netstats url
* @param ethStatsConnectOptions the netstats url
* @param blockchainQueries the blockchain queries
* @param protocolManager the protocol manager
* @param transactionPool the transaction pool
@ -126,7 +126,7 @@ public class EthStatsService {
* @param p2PNetwork the p 2 p network
*/
public EthStatsService(
final NetstatsUrl netstatsUrl,
final EthStatsConnectOptions ethStatsConnectOptions,
final BlockchainQueries blockchainQueries,
final EthProtocolManager protocolManager,
final TransactionPool transactionPool,
@ -136,7 +136,7 @@ public class EthStatsService {
final String clientVersion,
final GenesisConfigOptions genesisConfigOptions,
final P2PNetwork p2PNetwork) {
this.netstatsUrl = netstatsUrl;
this.ethStatsConnectOptions = ethStatsConnectOptions;
this.blockchainQueries = blockchainQueries;
this.protocolManager = protocolManager;
this.transactionPool = transactionPool;
@ -147,22 +147,43 @@ public class EthStatsService {
this.genesisConfigOptions = genesisConfigOptions;
this.p2PNetwork = p2PNetwork;
this.blockResultFactory = new BlockResultFactory();
this.httpClientOptions = new HttpClientOptions();
this.webSocketConnectOptions =
new WebSocketConnectOptions()
.setURI("/api")
.setHost(netstatsUrl.getHost())
.setPort(netstatsUrl.getPort())
.setSsl(true);
this.httpClientOptions = buildHttpClientOptions(ethStatsConnectOptions);
this.webSocketConnectOptions = buildWebSocketConnectOptions(ethStatsConnectOptions);
}
private static HttpClientOptions buildHttpClientOptions(
final EthStatsConnectOptions ethStatsConnectOptions) {
final HttpClientOptions options = new HttpClientOptions();
if (ethStatsConnectOptions.getCaCert() != null) {
options.setPemTrustOptions(
new PemTrustOptions().addCertPath(ethStatsConnectOptions.getCaCert().toString()));
}
return options;
}
private static WebSocketConnectOptions buildWebSocketConnectOptions(
final EthStatsConnectOptions ethStatsConnectOptions) {
return new WebSocketConnectOptions()
.setURI("/api")
.setSsl(true)
.setHost(ethStatsConnectOptions.getHost())
.setPort(getWsPort(ethStatsConnectOptions, true));
}
private static int getWsPort(
final EthStatsConnectOptions ethStatsConnectOptions, final boolean isSSL) {
if (ethStatsConnectOptions.getPort() >= 0) {
return ethStatsConnectOptions.getPort();
}
return isSSL ? 443 : 80;
}
/** Start. */
public void start() {
LOG.debug("Connecting to EthStats: {}", getEthStatsHost());
try {
enodeURL = p2PNetwork.getLocalEnode().orElseThrow();
vertx
.createHttpClient(httpClientOptions)
.webSocket(
@ -196,13 +217,8 @@ public class EthStatsService {
// sending a hello to initiate the connection using the secret
sendHello();
} else {
String errorMessage =
"Failed to reach the ethstats server " + event.cause().getMessage();
if (event.cause() instanceof SSLHandshakeException) {
webSocketConnectOptions.setSsl(false);
errorMessage += " (trying without ssl)";
}
LOG.error(errorMessage);
LOG.error(
"Failed to reach the ethstats server due to: {}", event.cause().getMessage());
retryInProgress.set(false);
retryConnect();
}
@ -213,6 +229,21 @@ public class EthStatsService {
}
}
private String getEthStatsHost() {
return String.format(
"%s://%s:%s",
webSocketConnectOptions.isSsl() ? "wss" : "ws",
ethStatsConnectOptions.getHost(),
getWsPort(ethStatsConnectOptions, webSocketConnectOptions.isSsl()));
}
/** Switch from ssl to non-ssl and vice-versa. Sets port to 443 or 80 if not specified. */
private void updateSSLProtocol() {
final boolean updatedSSL = !webSocketConnectOptions.isSsl();
webSocketConnectOptions.setSsl(updatedSSL);
webSocketConnectOptions.setPort(getWsPort(ethStatsConnectOptions, updatedSSL));
}
/** Ends the current web socket connection, observers and schedulers */
public void stop() {
if (webSocket != null && !webSocket.isClosed()) {
@ -227,6 +258,7 @@ public class EthStatsService {
private void retryConnect() {
if (retryInProgress.getAndSet(true) == FALSE) {
stop();
updateSSLProtocol(); // switch from ssl:true to ssl:false and vice-versa
protocolManager
.ethContext()
.getScheduler()
@ -245,7 +277,7 @@ public class EthStatsService {
final NodeInfo nodeInfo =
ImmutableNodeInfo.of(
netstatsUrl.getNodeName(),
ethStatsConnectOptions.getNodeName(),
clientVersion,
String.valueOf(port.get()),
chainId.get().toString(),
@ -255,13 +287,15 @@ public class EthStatsService {
arch,
"0.1.1",
true,
netstatsUrl.getContact());
ethStatsConnectOptions.getContact());
final EthStatsRequest hello =
new EthStatsRequest(
HELLO,
ImmutableAuthenticationData.of(
enodeURL.getNodeId().toHexString(), nodeInfo, netstatsUrl.getSecret()));
enodeURL.getNodeId().toHexString(),
nodeInfo,
ethStatsConnectOptions.getSecret()));
sendMessage(
webSocket,
hello,

@ -16,15 +16,17 @@ package org.hyperledger.besu.ethstats.util;
import static com.google.common.base.Preconditions.checkArgument;
import java.nio.file.Path;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.annotation.Nullable;
import org.immutables.value.Value;
import org.slf4j.LoggerFactory;
@Value.Immutable
public interface NetstatsUrl {
public interface EthStatsConnectOptions {
Pattern NETSTATS_URL_REGEX = Pattern.compile("([-\\w]+):([-\\w]+)?@([-.\\w]+)(:([\\d]+))?");
@ -38,23 +40,28 @@ public interface NetstatsUrl {
String getContact();
static NetstatsUrl fromParams(final String url, final String contact) {
@Nullable
Path getCaCert();
static EthStatsConnectOptions fromParams(
final String url, final String contact, final Path caCert) {
try {
checkArgument(url != null && !url.trim().isEmpty(), "Invalid empty value.");
final Matcher netStatsUrl = NETSTATS_URL_REGEX.matcher(url);
if (netStatsUrl.matches()) {
return ImmutableNetstatsUrl.builder()
return ImmutableEthStatsConnectOptions.builder()
.nodeName(netStatsUrl.group(1))
.secret(netStatsUrl.group(2))
.host(netStatsUrl.group(3))
.port(Integer.parseInt(Optional.ofNullable(netStatsUrl.group(5)).orElse("3000")))
.port(Integer.parseInt(Optional.ofNullable(netStatsUrl.group(5)).orElse("-1")))
.contact(contact)
.caCert(caCert)
.build();
}
} catch (IllegalArgumentException e) {
LoggerFactory.getLogger(NetstatsUrl.class).error(e.getMessage());
LoggerFactory.getLogger(EthStatsConnectOptions.class).error(e.getMessage());
}
throw new IllegalArgumentException(
"Invalid netstats URL syntax. Netstats URL should have the following format 'nodename:secret@host:port' or 'nodename:secret@host'.");

@ -33,8 +33,8 @@ import org.hyperledger.besu.ethereum.p2p.network.P2PNetwork;
import org.hyperledger.besu.ethereum.p2p.peers.EnodeURLImpl;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.Capability;
import org.hyperledger.besu.ethstats.request.EthStatsRequest;
import org.hyperledger.besu.ethstats.util.ImmutableNetstatsUrl;
import org.hyperledger.besu.ethstats.util.NetstatsUrl;
import org.hyperledger.besu.ethstats.util.EthStatsConnectOptions;
import org.hyperledger.besu.ethstats.util.ImmutableEthStatsConnectOptions;
import org.hyperledger.besu.plugin.data.EnodeURL;
import java.math.BigInteger;
@ -74,8 +74,8 @@ public class EthStatsServiceTest {
@Mock private HttpClient httpClient;
@Mock private WebSocket webSocket;
final NetstatsUrl netstatsUrl =
ImmutableNetstatsUrl.builder()
final EthStatsConnectOptions ethStatsConnectOptions =
ImmutableEthStatsConnectOptions.builder()
.nodeName("besu-node")
.secret("secret")
.host("127.0.0.1")
@ -108,7 +108,7 @@ public class EthStatsServiceTest {
public void shouldRetryWhenLocalEnodeNotAvailable() throws Exception {
ethStatsService =
new EthStatsService(
netstatsUrl,
ethStatsConnectOptions,
blockchainQueries,
ethProtocolManager,
transactionPool,
@ -127,7 +127,7 @@ public class EthStatsServiceTest {
public void shouldSendHelloMessage() {
ethStatsService =
new EthStatsService(
netstatsUrl,
ethStatsConnectOptions,
blockchainQueries,
ethProtocolManager,
transactionPool,
@ -160,7 +160,7 @@ public class EthStatsServiceTest {
ethStatsService =
new EthStatsService(
netstatsUrl,
ethStatsConnectOptions,
blockchainQueries,
ethProtocolManager,
transactionPool,
@ -192,7 +192,7 @@ public class EthStatsServiceTest {
public void shouldSendFullReportIfHelloMessageSucceeded() {
ethStatsService =
new EthStatsService(
netstatsUrl,
ethStatsConnectOptions,
blockchainQueries,
ethProtocolManager,
transactionPool,

@ -0,0 +1,110 @@
/*
* Copyright Hyperledger Besu Contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethstats.util;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import java.nio.file.Path;
import org.junit.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
public class EthStatsConnectOptionsTest {
private final String VALID_NETSTATS_URL = "Dev-Node-1:secret-with-dashes@127.0.0.1:3001";
private final String CONTACT = "contact@mail.fr";
private final String ERROR_MESSAGE =
"Invalid netstats URL syntax. Netstats URL should have the following format 'nodename:secret@host:port' or 'nodename:secret@host'.";
@Test
public void buildWithValidParams() {
final Path caCert = Path.of("./test.pem");
final EthStatsConnectOptions ethStatsConnectOptions =
EthStatsConnectOptions.fromParams(VALID_NETSTATS_URL, CONTACT, caCert);
assertThat(ethStatsConnectOptions.getHost()).isEqualTo("127.0.0.1");
assertThat(ethStatsConnectOptions.getNodeName()).isEqualTo("Dev-Node-1");
assertThat(ethStatsConnectOptions.getPort()).isEqualTo(3001);
assertThat(ethStatsConnectOptions.getSecret()).isEqualTo("secret-with-dashes");
assertThat(ethStatsConnectOptions.getContact()).isEqualTo(CONTACT);
assertThat(ethStatsConnectOptions.getCaCert()).isEqualTo(caCert);
}
@ParameterizedTest(name = "#{index} - With Host {0}")
@ValueSource(strings = {"url-test.test.com", "url.test.com", "test.com", "10.10.10.15"})
public void buildWithValidHost(final String host) {
final EthStatsConnectOptions ethStatsConnectOptions =
EthStatsConnectOptions.fromParams("Dev-Node-1:secret@" + host + ":3001", CONTACT, null);
assertThat(ethStatsConnectOptions.getHost()).isEqualTo(host);
}
@ParameterizedTest(name = "#{index} - With Host {0}")
@ValueSource(strings = {"url-test.test.com", "url.test.com", "test.com", "10.10.10.15"})
public void buildWithValidHostWithoutPort(final String host) {
final EthStatsConnectOptions ethStatsConnectOptions =
EthStatsConnectOptions.fromParams("Dev-Node-1:secret@" + host, CONTACT, null);
assertThat(ethStatsConnectOptions.getHost()).isEqualTo(host);
assertThat(ethStatsConnectOptions.getPort()).isEqualTo(-1);
}
@Test
public void shouldDetectEmptyParams() {
assertThatThrownBy(() -> EthStatsConnectOptions.fromParams("", CONTACT, null))
.isInstanceOf(IllegalArgumentException.class)
.hasMessageEndingWith(ERROR_MESSAGE);
}
@Test
public void shouldDetectMissingParams() {
// missing node name
assertThatThrownBy(
() -> EthStatsConnectOptions.fromParams("secret@127.0.0.1:3001", CONTACT, null))
.isInstanceOf(IllegalArgumentException.class)
.hasMessageEndingWith(ERROR_MESSAGE);
// missing host
assertThatThrownBy(() -> EthStatsConnectOptions.fromParams("Dev-Node-1:secret@", CONTACT, null))
.isInstanceOf(IllegalArgumentException.class)
.hasMessageEndingWith(ERROR_MESSAGE);
// missing port
assertThatThrownBy(
() -> EthStatsConnectOptions.fromParams("Dev-Node-1:secret@127.0.0.1:", CONTACT, null))
.isInstanceOf(IllegalArgumentException.class)
.hasMessageEndingWith(ERROR_MESSAGE);
}
@Test
public void shouldDetectInvalidParams() {
// invalid host
assertThatThrownBy(
() ->
EthStatsConnectOptions.fromParams(
"Dev-Node-1:secret@127.0@0.1:3001", CONTACT, null))
.isInstanceOf(IllegalArgumentException.class)
.hasMessageEndingWith(ERROR_MESSAGE);
// invalid port
assertThatThrownBy(
() ->
EthStatsConnectOptions.fromParams(
"Dev-Node-1:secret@127.0.0.1:A001", CONTACT, null))
.isInstanceOf(IllegalArgumentException.class)
.hasMessageEndingWith(ERROR_MESSAGE);
}
}

@ -1,100 +0,0 @@
/*
* Copyright ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethstats.util;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import org.junit.Test;
public class NetstatsUrlTest {
private final String VALID_NETSTATS_URL = "Dev-Node-1:secret-with-dashes@127.0.0.1:3001";
private final String CONTACT = "contact@mail.fr";
private final String ERROR_MESSAGE =
"Invalid netstats URL syntax. Netstats URL should have the following format 'nodename:secret@host:port' or 'nodename:secret@host'.";
@Test
public void buildWithValidParams() {
final NetstatsUrl netstatsUrl = NetstatsUrl.fromParams(VALID_NETSTATS_URL, CONTACT);
assertThat(netstatsUrl.getHost()).isEqualTo("127.0.0.1");
assertThat(netstatsUrl.getNodeName()).isEqualTo("Dev-Node-1");
assertThat(netstatsUrl.getPort()).isEqualTo(3001);
assertThat(netstatsUrl.getSecret()).isEqualTo("secret-with-dashes");
assertThat(netstatsUrl.getContact()).isEqualTo(CONTACT);
}
@Test
public void buildWithValidHost() {
final String[] validHosts =
new String[] {"url-test.test.com", "url.test.com", "test.com", "10.10.10.15"};
for (String host : validHosts) {
final NetstatsUrl netstatsUrl =
NetstatsUrl.fromParams("Dev-Node-1:secret@" + host + ":3001", CONTACT);
assertThat(netstatsUrl.getHost()).isEqualTo(host);
}
}
@Test
public void buildWithValidHostWithoutPort() {
final String[] validHosts =
new String[] {"url-test.test.com", "url.test.com", "test.com", "10.10.10.15"};
for (String host : validHosts) {
final NetstatsUrl netstatsUrl = NetstatsUrl.fromParams("Dev-Node-1:secret@" + host, CONTACT);
assertThat(netstatsUrl.getHost()).isEqualTo(host);
assertThat(netstatsUrl.getPort()).isEqualTo(3000);
}
}
@Test
public void shouldDetectEmptyParams() {
assertThatThrownBy(() -> NetstatsUrl.fromParams("", CONTACT))
.isInstanceOf(IllegalArgumentException.class)
.hasMessageEndingWith(ERROR_MESSAGE);
}
@Test
public void shouldDetectMissingParams() {
// missing node name
assertThatThrownBy(() -> NetstatsUrl.fromParams("secret@127.0.0.1:3001", CONTACT))
.isInstanceOf(IllegalArgumentException.class)
.hasMessageEndingWith(ERROR_MESSAGE);
// missing host
assertThatThrownBy(() -> NetstatsUrl.fromParams("Dev-Node-1:secret@", CONTACT))
.isInstanceOf(IllegalArgumentException.class)
.hasMessageEndingWith(ERROR_MESSAGE);
// missing port
assertThatThrownBy(() -> NetstatsUrl.fromParams("Dev-Node-1:secret@127.0.0.1:", CONTACT))
.isInstanceOf(IllegalArgumentException.class)
.hasMessageEndingWith(ERROR_MESSAGE);
}
@Test
public void shouldDetectInvalidParams() {
// invalid host
assertThatThrownBy(() -> NetstatsUrl.fromParams("Dev-Node-1:secret@127.0@0.1:3001", CONTACT))
.isInstanceOf(IllegalArgumentException.class)
.hasMessageEndingWith(ERROR_MESSAGE);
// invalid port
assertThatThrownBy(() -> NetstatsUrl.fromParams("Dev-Node-1:secret@127.0.0.1:A001", CONTACT))
.isInstanceOf(IllegalArgumentException.class)
.hasMessageEndingWith(ERROR_MESSAGE);
}
}
Loading…
Cancel
Save