Skip to content
This repository has been archived by the owner on Sep 26, 2019. It is now read-only.

[PAN-2265] Expose fast-sync options on command line #1218

Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
*/
package tech.pegasys.pantheon.ethereum.eth.sync.fastsync;

import static java.lang.String.format;
import static java.util.concurrent.CompletableFuture.completedFuture;
import static tech.pegasys.pantheon.ethereum.eth.sync.fastsync.FastSyncError.CHAIN_TOO_SHORT;
import static tech.pegasys.pantheon.util.FutureUtils.completedExceptionally;
Expand Down Expand Up @@ -71,8 +72,24 @@ public CompletableFuture<FastSyncState> waitForSuitablePeers(final FastSyncState
ethContext, syncConfig.getFastSyncMinimumPeerCount(), metricsSystem);

final EthScheduler scheduler = ethContext.getScheduler();
final CompletableFuture<Void> fastSyncTask;
if (!syncConfig.getFastSyncMaximumPeerWaitTime().isZero()) {
LOG.info(
AbdelStark marked this conversation as resolved.
Show resolved Hide resolved
format(
AbdelStark marked this conversation as resolved.
Show resolved Hide resolved
"Waiting for at least %d peers, maximum wait time set to %s.",
syncConfig.getFastSyncMinimumPeerCount(),
syncConfig.getFastSyncMaximumPeerWaitTime().toString()));
fastSyncTask =
scheduler.timeout(waitForPeersTask, syncConfig.getFastSyncMaximumPeerWaitTime());
} else {
LOG.info(
format(
AbdelStark marked this conversation as resolved.
Show resolved Hide resolved
"Waiting for at least %d peers, no maximum wait time set.",
syncConfig.getFastSyncMinimumPeerCount()));
fastSyncTask = scheduler.scheduleServiceTask(waitForPeersTask);
}
return exceptionallyCompose(
scheduler.timeout(waitForPeersTask, syncConfig.getFastSyncMaximumPeerWaitTime()),
fastSyncTask,
error -> {
if (ExceptionUtils.rootCause(error) instanceof TimeoutException) {
if (ethContext.getEthPeers().availablePeerCount() > 0) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ public interface DefaultCommandValues {
// Default should be FAST for the next release
// but we use FULL for the moment as Fast is still in progress
SyncMode DEFAULT_SYNC_MODE = SyncMode.FULL;
int FAST_SYNC_MAX_WAIT_TIME = 0;
int FAST_SYNC_MIN_PEER_COUNT = 5;
AbdelStark marked this conversation as resolved.
Show resolved Hide resolved
int DEFAULT_MAX_PEERS = 25;

static Path getDefaultPantheonDataPath(final Object command) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@

import static com.google.common.base.Preconditions.checkNotNull;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.time.Duration.ofSeconds;
AbdelStark marked this conversation as resolved.
Show resolved Hide resolved
import static java.util.Arrays.asList;
import static tech.pegasys.pantheon.cli.CommandLineUtils.checkOptionDependencies;
import static tech.pegasys.pantheon.cli.DefaultCommandValues.getDefaultPantheonDataPath;
import static tech.pegasys.pantheon.cli.NetworkName.MAINNET;
import static tech.pegasys.pantheon.ethereum.jsonrpc.JsonRpcConfiguration.DEFAULT_JSON_RPC_PORT;
Expand Down Expand Up @@ -73,7 +76,6 @@
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
Expand Down Expand Up @@ -221,13 +223,28 @@ void setBootnodes(final List<String> values) {
private final Collection<String> bannedNodeIds = new ArrayList<>();

@Option(
hidden = true,
AbdelStark marked this conversation as resolved.
Show resolved Hide resolved
names = {"--sync-mode"},
paramLabel = MANDATORY_MODE_FORMAT_HELP,
description =
"Synchronization mode, possible values are ${COMPLETION-CANDIDATES} (default: ${DEFAULT-VALUE})")
private final SyncMode syncMode = DEFAULT_SYNC_MODE;

@Option(
hidden = true,
AbdelStark marked this conversation as resolved.
Show resolved Hide resolved
names = {"--fast-sync-min-peers"},
paramLabel = MANDATORY_INTEGER_FORMAT_HELP,
description =
"Minimum number of peers before starting fast sync. (default: ${DEFAULT-VALUE})")
AbdelStark marked this conversation as resolved.
Show resolved Hide resolved
private final Integer fastSyncMinPeerCount = FAST_SYNC_MIN_PEER_COUNT;

@Option(
hidden = true,
names = {"--fast-sync-max-wait-time"},
paramLabel = MANDATORY_INTEGER_FORMAT_HELP,
description =
"Maximum time to wait the required number of peers before starting fast sync, expressed in seconds, 0 means no timeout(default: ${DEFAULT-VALUE})")
AbdelStark marked this conversation as resolved.
Show resolved Hide resolved
private final Integer fastSyncMaxWaitTime = FAST_SYNC_MAX_WAIT_TIME;

@Option(
names = {"--network"},
paramLabel = MANDATORY_NETWORK_FORMAT_HELP,
Expand Down Expand Up @@ -590,25 +607,28 @@ public void run() {
}

// Check that P2P options are able to work or send an error
CommandLineUtils.checkOptionDependencies(
checkOptionDependencies(
AbdelStark marked this conversation as resolved.
Show resolved Hide resolved
logger,
commandLine,
"--p2p-enabled",
!p2pEnabled,
Arrays.asList(
asList(
"--bootnodes",
"--discovery-enabled",
"--max-peers",
"--banned-node-id",
"--banned-node-ids"));

// Check that mining options are able to work or send an error
CommandLineUtils.checkOptionDependencies(
checkOptionDependencies(
logger,
commandLine,
"--miner-enabled",
!isMiningEnabled,
Arrays.asList("--miner-coinbase", "--min-gas-price", "--miner-extra-data"));
asList("--miner-coinbase", "--min-gas-price", "--miner-extra-data"));

// Check that fast sync options are able to work or send an error
checkFastSyncOptions();

//noinspection ConstantConditions
if (isMiningEnabled && coinbase == null) {
Expand Down Expand Up @@ -658,6 +678,19 @@ public void run() {
}
}

private void checkFastSyncOptions() throws ParameterException {
if (fastSyncMaxWaitTime < 0) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Our parameter validation has been more just in time and directly in the configuration options rather than directly between the CLI and the config. It keeps the validations closer to where the parameters are actually stored. Validating in the CLI also provides a barrier to splitting the code apart.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A custom converter to restrict the integer to a positive non null one ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

None of other integers have restriction. For example metrics interval in seconds are not checked too. So what is the best practice to perform those checks ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest we keep like this for this PR. We need to define a way to do this kind of checks and implement it for all options in a separate ticket imo.

throw new ParameterException(
commandLine, "--fast-sync-max-wait-time must be a positive or zero integer");
AbdelStark marked this conversation as resolved.
Show resolved Hide resolved
AbdelStark marked this conversation as resolved.
Show resolved Hide resolved
}
checkOptionDependencies(
AbdelStark marked this conversation as resolved.
Show resolved Hide resolved
logger,
commandLine,
"--sync-mode",
SyncMode.FAST.equals(syncMode),
asList("--fast-sync-num-peers", "--fast-sync-timeout"));
}

private NetworkName getNetwork() {
//noinspection ConstantConditions network is not always null but injected by PicoCLI if used
return network == null ? MAINNET : network;
Expand Down Expand Up @@ -697,12 +730,12 @@ PantheonController<?> buildController() {

private JsonRpcConfiguration jsonRpcConfiguration() {

CommandLineUtils.checkOptionDependencies(
checkOptionDependencies(
logger,
commandLine,
"--rpc-http-enabled",
!isRpcHttpEnabled,
Arrays.asList(
asList(
"--rpc-http-api",
"--rpc-http-apis",
"--rpc-http-cors-origins",
Expand Down Expand Up @@ -731,12 +764,12 @@ private JsonRpcConfiguration jsonRpcConfiguration() {

private WebSocketConfiguration webSocketConfiguration() {

CommandLineUtils.checkOptionDependencies(
checkOptionDependencies(
logger,
commandLine,
"--rpc-ws-enabled",
!isRpcWsEnabled,
Arrays.asList(
asList(
"--rpc-ws-api",
"--rpc-ws-apis",
"--rpc-ws-host",
Expand Down Expand Up @@ -769,19 +802,19 @@ MetricsConfiguration metricsConfiguration() {
+ "time. Please refer to CLI reference for more details about this constraint.");
}

CommandLineUtils.checkOptionDependencies(
checkOptionDependencies(
logger,
commandLine,
"--metrics-enabled",
!isMetricsEnabled,
Arrays.asList("--metrics-host", "--metrics-port"));
asList("--metrics-host", "--metrics-port"));

CommandLineUtils.checkOptionDependencies(
checkOptionDependencies(
logger,
commandLine,
"--metrics-push-enabled",
!isMetricsPushEnabled,
Arrays.asList(
asList(
"--metrics-push-host",
"--metrics-push-port",
"--metrics-push-interval",
Expand Down Expand Up @@ -882,13 +915,12 @@ private boolean contractPermissionsEnabled() {

private PrivacyParameters privacyParameters() throws IOException {

CommandLineUtils.checkOptionDependencies(
checkOptionDependencies(
logger,
commandLine,
"--privacy-enabled",
!isPrivacyEnabled,
Arrays.asList(
"--privacy-url", "--privacy-public-key-file", "--privacy-precompiled-address"));
asList("--privacy-url", "--privacy-public-key-file", "--privacy-precompiled-address"));

final PrivacyParameters privacyParameters = PrivacyParameters.noPrivacy();
if (isPrivacyEnabled) {
Expand All @@ -909,6 +941,8 @@ private PrivacyParameters privacyParameters() throws IOException {
private SynchronizerConfiguration buildSyncConfig() {
return synchronizerConfigurationBuilder
.syncMode(syncMode)
.fastSyncMinimumPeerCount(fastSyncMinPeerCount)
.fastSyncMaximumPeerWaitTime(ofSeconds(fastSyncMaxWaitTime))
.maxTrailingPeers(TrailingPeerRequirements.calculateMaxTrailingPeers(maxPeers))
.build();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import java.io.InputStream;
import java.io.PrintStream;
import java.nio.file.Path;
import java.time.Duration;
import java.util.Collection;

import org.apache.logging.log4j.LogManager;
Expand Down Expand Up @@ -110,6 +111,10 @@ public void initMocks() throws Exception {

when(mockSyncConfBuilder.syncMode(any())).thenReturn(mockSyncConfBuilder);
when(mockSyncConfBuilder.maxTrailingPeers(anyInt())).thenReturn(mockSyncConfBuilder);
when(mockSyncConfBuilder.fastSyncMinimumPeerCount(anyInt())).thenReturn(mockSyncConfBuilder);
when(mockSyncConfBuilder.fastSyncMaximumPeerWaitTime(any(Duration.class)))
.thenReturn(mockSyncConfBuilder);

when(mockSyncConfBuilder.build()).thenReturn(mockSyncConf);

when(mockRunnerBuilder.vertx(any())).thenReturn(mockRunnerBuilder);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.Duration;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
Expand All @@ -73,7 +74,6 @@
import net.consensys.cava.toml.Toml;
import net.consensys.cava.toml.TomlParseResult;
import org.apache.commons.text.StringEscapeUtils;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
Expand Down Expand Up @@ -1035,7 +1035,6 @@ public void maxpeersOptionMustBeUsed() {
assertThat(commandErrorOutput.toString()).isEmpty();
}

@Ignore("Ignored as we only have one mode available for now. See NC-1057/NC-1681")
AbdelStark marked this conversation as resolved.
Show resolved Hide resolved
@Test
public void syncModeOptionMustBeUsed() {
AbdelStark marked this conversation as resolved.
Show resolved Hide resolved

Expand All @@ -1049,6 +1048,27 @@ public void syncModeOptionMustBeUsed() {
assertThat(commandErrorOutput.toString()).isEmpty();
}

@Test
AbdelStark marked this conversation as resolved.
Show resolved Hide resolved
public void fastSyncTimeoutOptionMustBeUsed() {
AbdelStark marked this conversation as resolved.
Show resolved Hide resolved

parseCommand("--sync-mode", "FAST", "--fast-sync-max-wait-time", "17");
verify(mockSyncConfBuilder).syncMode(ArgumentMatchers.eq(SyncMode.FAST));
verify(mockSyncConfBuilder)
.fastSyncMaximumPeerWaitTime(ArgumentMatchers.eq(Duration.ofSeconds(17)));
assertThat(commandOutput.toString()).isEmpty();
assertThat(commandErrorOutput.toString()).isEmpty();
}

@Test
public void fastSyncMinPeersOptionMustBeUsed() {

parseCommand("--sync-mode", "FAST", "--fast-sync-min-peers", "11");
verify(mockSyncConfBuilder).syncMode(ArgumentMatchers.eq(SyncMode.FAST));
verify(mockSyncConfBuilder).fastSyncMinimumPeerCount(ArgumentMatchers.eq(11));
assertThat(commandOutput.toString()).isEmpty();
assertThat(commandErrorOutput.toString()).isEmpty();
}

@Test
public void rpcHttpEnabledPropertyDefaultIsFalse() {
parseCommand();
Expand Down
2 changes: 2 additions & 0 deletions pantheon/src/test/resources/everything_config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ host-whitelist=["all"]
network="MAINNET"
genesis-file="~/genesis.json"
sync-mode="fast"
fast-sync-min-peers=5
fast-sync-max-wait-time=30
network-id=303

# JSON-RPC
Expand Down