diff --git a/config/src/main/java/tech/pegasys/pantheon/config/GenesisConfigFile.java b/config/src/main/java/tech/pegasys/pantheon/config/GenesisConfigFile.java index 07c639dfe7..b9cf623d19 100644 --- a/config/src/main/java/tech/pegasys/pantheon/config/GenesisConfigFile.java +++ b/config/src/main/java/tech/pegasys/pantheon/config/GenesisConfigFile.java @@ -16,6 +16,8 @@ import static java.nio.charset.StandardCharsets.UTF_8; import static tech.pegasys.pantheon.config.JsonUtil.normalizeKeys; import java.io.IOException; +import java.util.Collections; +import java.util.Map; import java.util.Optional; import java.util.stream.Stream; @@ -82,9 +84,13 @@ public class GenesisConfigFile { } public GenesisConfigOptions getConfigOptions() { + return getConfigOptions(Collections.emptyMap()); + } + + public GenesisConfigOptions getConfigOptions(final Map overrides) { ObjectNode config = JsonUtil.getObjectNode(configRoot, "config").orElse(JsonUtil.createEmptyObjectNode()); - return new JsonGenesisConfigOptions(config); + return new JsonGenesisConfigOptions(config, overrides); } public Stream streamAllocations() { diff --git a/config/src/main/java/tech/pegasys/pantheon/config/JsonGenesisConfigOptions.java b/config/src/main/java/tech/pegasys/pantheon/config/JsonGenesisConfigOptions.java index 22b71e88b5..552029fb8a 100644 --- a/config/src/main/java/tech/pegasys/pantheon/config/JsonGenesisConfigOptions.java +++ b/config/src/main/java/tech/pegasys/pantheon/config/JsonGenesisConfigOptions.java @@ -15,10 +15,12 @@ package tech.pegasys.pantheon.config; import static java.util.Objects.isNull; import java.math.BigInteger; +import java.util.Collections; import java.util.Map; import java.util.Optional; import java.util.OptionalInt; import java.util.OptionalLong; +import java.util.TreeMap; import com.fasterxml.jackson.databind.node.ObjectNode; import com.google.common.collect.ImmutableMap; @@ -30,13 +32,22 @@ public class JsonGenesisConfigOptions implements GenesisConfigOptions { private static final String IBFT2_CONFIG_KEY = "ibft2"; private static final String CLIQUE_CONFIG_KEY = "clique"; private final ObjectNode configRoot; + private final Map configOverrides = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); public static JsonGenesisConfigOptions fromJsonObject(final ObjectNode configRoot) { return new JsonGenesisConfigOptions(configRoot); } - JsonGenesisConfigOptions(final ObjectNode maybeConfig) { + private JsonGenesisConfigOptions(final ObjectNode maybeConfig) { + this(maybeConfig, Collections.emptyMap()); + } + + JsonGenesisConfigOptions( + final ObjectNode maybeConfig, final Map configOverrides) { this.configRoot = isNull(maybeConfig) ? JsonUtil.createEmptyObjectNode() : maybeConfig; + if (configOverrides != null) { + this.configOverrides.putAll(configOverrides); + } } @Override @@ -148,17 +159,17 @@ public class JsonGenesisConfigOptions implements GenesisConfigOptions { @Override public Optional getChainId() { - return JsonUtil.getValueAsString(configRoot, "chainid").map(BigInteger::new); + return getOptionalBigInteger("chainid"); } @Override public OptionalInt getContractSizeLimit() { - return JsonUtil.getInt(configRoot, "contractsizelimit"); + return getOptionalInt("contractsizelimit"); } @Override public OptionalInt getEvmStackSize() { - return JsonUtil.getInt(configRoot, "evmstacksize"); + return getOptionalInt("evmstacksize"); } @Override @@ -176,9 +187,8 @@ public class JsonGenesisConfigOptions implements GenesisConfigOptions { .ifPresent( l -> { builder.put("eip150Block", l); - if (configRoot.has("eip150hash")) { - builder.put("eip150Hash", configRoot.get("eip150hash").asText()); - } + getOptionalString("eip150hash") + .ifPresent(eip150hash -> builder.put("eip150Hash", eip150hash)); }); getSpuriousDragonBlockNumber() .ifPresent( @@ -207,7 +217,45 @@ public class JsonGenesisConfigOptions implements GenesisConfigOptions { return builder.build(); } + private Optional getOptionalString(final String key) { + if (configOverrides.containsKey(key)) { + final String value = configOverrides.get(key); + return value == null || value.isEmpty() ? Optional.empty() : Optional.of(value); + } else { + return JsonUtil.getString(configRoot, key); + } + } + private OptionalLong getOptionalLong(final String key) { - return JsonUtil.getLong(configRoot, key); + if (configOverrides.containsKey(key)) { + final String value = configOverrides.get(key); + return value == null || value.isEmpty() + ? OptionalLong.empty() + : OptionalLong.of(Long.valueOf(configOverrides.get(key), 10)); + } else { + return JsonUtil.getLong(configRoot, key); + } + } + + private OptionalInt getOptionalInt(final String key) { + if (configOverrides.containsKey(key)) { + final String value = configOverrides.get(key); + return value == null || value.isEmpty() + ? OptionalInt.empty() + : OptionalInt.of(Integer.valueOf(configOverrides.get(key), 10)); + } else { + return JsonUtil.getInt(configRoot, key); + } + } + + private Optional getOptionalBigInteger(final String key) { + if (configOverrides.containsKey(key)) { + final String value = configOverrides.get(key); + return value == null || value.isEmpty() + ? Optional.empty() + : Optional.of(new BigInteger(value)); + } else { + return JsonUtil.getValueAsString(configRoot, key).map(s -> new BigInteger(s, 10)); + } } } diff --git a/config/src/test/java/tech/pegasys/pantheon/config/GenesisConfigFileTest.java b/config/src/test/java/tech/pegasys/pantheon/config/GenesisConfigFileTest.java index 2c06360564..7a4f747f74 100644 --- a/config/src/test/java/tech/pegasys/pantheon/config/GenesisConfigFileTest.java +++ b/config/src/test/java/tech/pegasys/pantheon/config/GenesisConfigFileTest.java @@ -16,7 +16,9 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import java.math.BigInteger; +import java.util.HashMap; import java.util.Map; +import java.util.TreeMap; import java.util.function.Function; import java.util.stream.Collectors; @@ -214,6 +216,78 @@ public class GenesisConfigFileTest { // Unfortunately there is no good (non-flakey) way to assert logs. } + @Test + public void testOverridePresent() { + final GenesisConfigFile config = GenesisConfigFile.development(); + final int bigBlock = 999_999_999; + final String bigBlockString = Integer.toString(bigBlock); + final Map override = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); + override.put("istanbulBlock", bigBlockString); + override.put("chainId", bigBlockString); + override.put("contractSizeLimit", bigBlockString); + + assertThat(config.getConfigOptions(override).getIstanbulBlockNumber()).hasValue(bigBlock); + assertThat(config.getConfigOptions(override).getChainId()) + .hasValue(BigInteger.valueOf(bigBlock)); + assertThat(config.getConfigOptions(override).getContractSizeLimit()).hasValue(bigBlock); + } + + @Test + public void testOverrideNull() { + final GenesisConfigFile config = GenesisConfigFile.development(); + final Map override = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); + override.put("istanbulBlock", null); + override.put("chainId", null); + override.put("contractSizeLimit", null); + + assertThat(config.getConfigOptions(override).getIstanbulBlockNumber()).isNotPresent(); + assertThat(config.getConfigOptions(override).getChainId()).isNotPresent(); + assertThat(config.getConfigOptions(override).getContractSizeLimit()).isNotPresent(); + } + + @Test + public void testOverrideCaseInsensitivity() { + final GenesisConfigFile config = GenesisConfigFile.development(); + final int bigBlock = 999_999_999; + final String bigBlockString = Integer.toString(bigBlock); + final Map override = new HashMap<>(); + // as speicified + override.put("istanbulBlock", bigBlockString); + // ALL CAPS + override.put("CHAINID", bigBlockString); + // all lower case + override.put("contractsizelimit", bigBlockString); + + assertThat(config.getConfigOptions(override).getIstanbulBlockNumber()).hasValue(bigBlock); + assertThat(config.getConfigOptions(override).getChainId()) + .hasValue(BigInteger.valueOf(bigBlock)); + assertThat(config.getConfigOptions(override).getContractSizeLimit()).hasValue(bigBlock); + } + + @Test + public void testOverrideEmptyString() { + final GenesisConfigFile config = GenesisConfigFile.development(); + final Map override = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); + override.put("istanbulBlock", ""); + override.put("chainId", ""); + override.put("contractSizeLimit", ""); + + assertThat(config.getConfigOptions(override).getIstanbulBlockNumber()).isNotPresent(); + assertThat(config.getConfigOptions(override).getChainId()).isNotPresent(); + assertThat(config.getConfigOptions(override).getContractSizeLimit()).isNotPresent(); + } + + @Test + public void testNoOverride() { + final GenesisConfigFile config = GenesisConfigFile.development(); + + assertThat(config.getConfigOptions().getConstantinopleFixBlockNumber()).hasValue(0); + assertThat(config.getConfigOptions().getIstanbulBlockNumber()).isNotPresent(); + assertThat(config.getConfigOptions().getChainId()).hasValue(BigInteger.valueOf(2018)); + assertThat(config.getConfigOptions().getContractSizeLimit()).hasValue(2147483647); + assertThat(config.getConfigOptions().getEvmStackSize()).isNotPresent(); + } + private GenesisConfigFile configWithProperty(final String key, final String value) { return GenesisConfigFile.fromConfig("{\"" + key + "\":\"" + value + "\"}"); } diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java b/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java index 12f107054d..2e5659b500 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java @@ -118,6 +118,7 @@ import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.TreeMap; import java.util.function.Supplier; import java.util.stream.Collectors; @@ -650,6 +651,16 @@ public class PantheonCommand implements DefaultCommandValues, Runnable { private final Integer pendingTxRetentionPeriod = TransactionPoolConfiguration.DEFAULT_TX_RETENTION_HOURS; + @Option( + names = {"--override-genesis-config"}, + paramLabel = "NAME=VALUE", + description = "Overrides configuration values in the genesis file. Use with care.", + arity = "*", + hidden = true, + split = ",") + private final Map genesisConfigOverrides = + new TreeMap<>(String.CASE_INSENSITIVE_ORDER); + private EthNetworkConfig ethNetworkConfig; private JsonRpcConfiguration jsonRpcConfiguration; private GraphQLConfiguration graphQLConfiguration; @@ -932,7 +943,7 @@ public class PantheonCommand implements DefaultCommandValues, Runnable { public PantheonControllerBuilder getControllerBuilder() { try { return controllerBuilderFactory - .fromEthNetworkConfig(updateNetworkConfig(getNetwork())) + .fromEthNetworkConfig(updateNetworkConfig(getNetwork()), genesisConfigOverrides) .synchronizerConfiguration(buildSyncConfig()) .ethProtocolConfiguration(ethProtocolOptions.toDomainObject()) .rocksDbConfiguration(buildRocksDbConfiguration()) @@ -946,7 +957,8 @@ public class PantheonCommand implements DefaultCommandValues, Runnable { .clock(Clock.systemUTC()) .isRevertReasonEnabled(isRevertReasonEnabled) .isPruningEnabled(isPruningEnabled) - .pruningConfiguration(buildPruningConfiguration()); + .pruningConfiguration(buildPruningConfiguration()) + .genesisConfigOverrides(genesisConfigOverrides); } catch (final IOException e) { throw new ExecutionException(this.commandLine, "Invalid path", e); } @@ -1351,7 +1363,7 @@ public class PantheonCommand implements DefaultCommandValues, Runnable { final GenesisConfigFile genesisConfigFile = GenesisConfigFile.fromConfig(genesisConfig()); builder.setNetworkId( genesisConfigFile - .getConfigOptions() + .getConfigOptions(genesisConfigOverrides) .getChainId() .orElse(EthNetworkConfig.getNetworkConfig(MAINNET).getNetworkId())); } catch (final DecodeException e) { diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/controller/CliquePantheonControllerBuilder.java b/pantheon/src/main/java/tech/pegasys/pantheon/controller/CliquePantheonControllerBuilder.java index 269b5e8427..5791d177bf 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/controller/CliquePantheonControllerBuilder.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/controller/CliquePantheonControllerBuilder.java @@ -59,7 +59,7 @@ public class CliquePantheonControllerBuilder extends PantheonControllerBuilder createProtocolSchedule() { return CliqueProtocolSchedule.create( - genesisConfig.getConfigOptions(), nodeKeys, privacyParameters, isRevertReasonEnabled); + genesisConfig.getConfigOptions(genesisConfigOverrides), + nodeKeys, + privacyParameters, + isRevertReasonEnabled); } @Override diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/controller/IbftLegacyPantheonControllerBuilder.java b/pantheon/src/main/java/tech/pegasys/pantheon/controller/IbftLegacyPantheonControllerBuilder.java index 2112e81106..727b0ce745 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/controller/IbftLegacyPantheonControllerBuilder.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/controller/IbftLegacyPantheonControllerBuilder.java @@ -64,14 +64,16 @@ public class IbftLegacyPantheonControllerBuilder extends PantheonControllerBuild @Override protected ProtocolSchedule createProtocolSchedule() { return IbftProtocolSchedule.create( - genesisConfig.getConfigOptions(), privacyParameters, isRevertReasonEnabled); + genesisConfig.getConfigOptions(genesisConfigOverrides), + privacyParameters, + isRevertReasonEnabled); } @Override protected IbftContext createConsensusContext( final Blockchain blockchain, final WorldStateArchive worldStateArchive) { final IbftConfigOptions ibftConfig = - genesisConfig.getConfigOptions().getIbftLegacyConfigOptions(); + genesisConfig.getConfigOptions(genesisConfigOverrides).getIbftLegacyConfigOptions(); final EpochManager epochManager = new EpochManager(ibftConfig.getEpochLength()); final VoteTallyCache voteTallyCache = new VoteTallyCache( diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/controller/IbftPantheonControllerBuilder.java b/pantheon/src/main/java/tech/pegasys/pantheon/controller/IbftPantheonControllerBuilder.java index e985947e52..ef5f2ae22b 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/controller/IbftPantheonControllerBuilder.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/controller/IbftPantheonControllerBuilder.java @@ -81,7 +81,7 @@ public class IbftPantheonControllerBuilder extends PantheonControllerBuilder createProtocolSchedule() { return IbftProtocolSchedule.create( - genesisConfig.getConfigOptions(), privacyParameters, isRevertReasonEnabled); + genesisConfig.getConfigOptions(genesisConfigOverrides), + privacyParameters, + isRevertReasonEnabled); } @Override @@ -226,7 +228,8 @@ public class IbftPantheonControllerBuilder extends PantheonControllerBuilder createProtocolSchedule() { return MainnetProtocolSchedule.fromConfig( - genesisConfig.getConfigOptions(), privacyParameters, isRevertReasonEnabled); + genesisConfig.getConfigOptions(genesisConfigOverrides), + privacyParameters, + isRevertReasonEnabled); } } diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/controller/PantheonController.java b/pantheon/src/main/java/tech/pegasys/pantheon/controller/PantheonController.java index c39193c833..0b75529f32 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/controller/PantheonController.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/controller/PantheonController.java @@ -29,6 +29,7 @@ import tech.pegasys.pantheon.ethereum.mainnet.ProtocolSchedule; import tech.pegasys.pantheon.ethereum.p2p.config.SubProtocolConfiguration; import java.util.Collection; +import java.util.Collections; import java.util.Map; public class PantheonController implements java.io.Closeable { @@ -129,12 +130,25 @@ public class PantheonController implements java.io.Closeable { public PantheonControllerBuilder fromEthNetworkConfig( final EthNetworkConfig ethNetworkConfig) { - return fromGenesisConfig(GenesisConfigFile.fromConfig(ethNetworkConfig.getGenesisConfig())) + return fromEthNetworkConfig(ethNetworkConfig, Collections.emptyMap()); + } + + public PantheonControllerBuilder fromEthNetworkConfig( + final EthNetworkConfig ethNetworkConfig, final Map genesisConfigOverrides) { + return fromGenesisConfig( + GenesisConfigFile.fromConfig(ethNetworkConfig.getGenesisConfig()), + genesisConfigOverrides) .networkId(ethNetworkConfig.getNetworkId()); } public PantheonControllerBuilder fromGenesisConfig(final GenesisConfigFile genesisConfig) { - final GenesisConfigOptions configOptions = genesisConfig.getConfigOptions(); + return fromGenesisConfig(genesisConfig, Collections.emptyMap()); + } + + public PantheonControllerBuilder fromGenesisConfig( + final GenesisConfigFile genesisConfig, final Map genesisConfigOverrides) { + final GenesisConfigOptions configOptions = + genesisConfig.getConfigOptions(genesisConfigOverrides); final PantheonControllerBuilder builder; if (configOptions.isEthHash()) { diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/controller/PantheonControllerBuilder.java b/pantheon/src/main/java/tech/pegasys/pantheon/controller/PantheonControllerBuilder.java index 6c4c2407ea..c855fbd862 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/controller/PantheonControllerBuilder.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/controller/PantheonControllerBuilder.java @@ -60,6 +60,7 @@ import java.time.Clock; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.OptionalLong; import java.util.concurrent.Executors; @@ -89,6 +90,7 @@ public abstract class PantheonControllerBuilder { private RocksDbConfiguration rocksDbConfiguration; private boolean isPruningEnabled; private PruningConfiguration pruningConfiguration; + Map genesisConfigOverrides; public PantheonControllerBuilder rocksDbConfiguration( final RocksDbConfiguration rocksDbConfiguration) { @@ -181,6 +183,12 @@ public abstract class PantheonControllerBuilder { return this; } + public PantheonControllerBuilder genesisConfigOverrides( + final Map genesisConfigOverrides) { + this.genesisConfigOverrides = genesisConfigOverrides; + return this; + } + public PantheonController build() throws IOException { checkNotNull(genesisConfig, "Missing genesis config"); checkNotNull(syncConfig, "Missing sync config"); @@ -272,7 +280,8 @@ public abstract class PantheonControllerBuilder { clock, metricsSystem); - final OptionalLong daoBlock = genesisConfig.getConfigOptions().getDaoForkBlock(); + final OptionalLong daoBlock = + genesisConfig.getConfigOptions(genesisConfigOverrides).getDaoForkBlock(); if (daoBlock.isPresent()) { // Setup dao validator final EthContext ethContext = ethProtocolManager.ethContext(); @@ -311,7 +320,7 @@ public abstract class PantheonControllerBuilder { protocolSchedule, protocolContext, ethProtocolManager, - genesisConfig.getConfigOptions(), + genesisConfig.getConfigOptions(genesisConfigOverrides), subProtocolConfiguration, synchronizer, additionalJsonRpcMethodFactory, diff --git a/pantheon/src/test/java/tech/pegasys/pantheon/cli/CommandTestAbstract.java b/pantheon/src/test/java/tech/pegasys/pantheon/cli/CommandTestAbstract.java index 6bb661749c..01b021b92b 100644 --- a/pantheon/src/test/java/tech/pegasys/pantheon/cli/CommandTestAbstract.java +++ b/pantheon/src/test/java/tech/pegasys/pantheon/cli/CommandTestAbstract.java @@ -139,7 +139,9 @@ public abstract class CommandTestAbstract { public void initMocks() throws Exception { // doReturn used because of generic PantheonController - doReturn(mockControllerBuilder).when(mockControllerBuilderFactory).fromEthNetworkConfig(any()); + doReturn(mockControllerBuilder) + .when(mockControllerBuilderFactory) + .fromEthNetworkConfig(any(), any()); when(mockControllerBuilder.synchronizerConfiguration(any())).thenReturn(mockControllerBuilder); when(mockControllerBuilder.ethProtocolConfiguration(any())).thenReturn(mockControllerBuilder); when(mockControllerBuilder.rocksDbConfiguration(any())).thenReturn(mockControllerBuilder); @@ -154,6 +156,7 @@ public abstract class CommandTestAbstract { when(mockControllerBuilder.isRevertReasonEnabled(false)).thenReturn(mockControllerBuilder); when(mockControllerBuilder.isPruningEnabled(anyBoolean())).thenReturn(mockControllerBuilder); when(mockControllerBuilder.pruningConfiguration(any())).thenReturn(mockControllerBuilder); + when(mockControllerBuilder.genesisConfigOverrides(any())).thenReturn(mockControllerBuilder); // doReturn used because of generic PantheonController doReturn(mockController).when(mockControllerBuilder).build(); diff --git a/pantheon/src/test/java/tech/pegasys/pantheon/cli/PantheonCommandTest.java b/pantheon/src/test/java/tech/pegasys/pantheon/cli/PantheonCommandTest.java index d0f0bb24be..9853eabe56 100644 --- a/pantheon/src/test/java/tech/pegasys/pantheon/cli/PantheonCommandTest.java +++ b/pantheon/src/test/java/tech/pegasys/pantheon/cli/PantheonCommandTest.java @@ -17,6 +17,7 @@ import static java.util.Arrays.asList; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assume.assumeFalse; import static org.junit.Assume.assumeTrue; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isNotNull; import static org.mockito.Mockito.atLeast; @@ -70,6 +71,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -168,7 +170,7 @@ public class PantheonCommandTest extends CommandTestAbstract { verify(mockRunnerBuilder).ethNetworkConfig(ethNetworkArg.capture()); verify(mockRunnerBuilder).build(); - verify(mockControllerBuilderFactory).fromEthNetworkConfig(ethNetworkArg.capture()); + verify(mockControllerBuilderFactory).fromEthNetworkConfig(ethNetworkArg.capture(), any()); final ArgumentCaptor miningArg = ArgumentCaptor.forClass(MiningParameters.class); verify(mockControllerBuilder).synchronizerConfiguration(syncConfigurationCaptor.capture()); @@ -333,7 +335,7 @@ public class PantheonCommandTest extends CommandTestAbstract { .setBootNodes(nodes) .build(); verify(mockControllerBuilder).dataDirectory(eq(Paths.get("/opt/pantheon").toAbsolutePath())); - verify(mockControllerBuilderFactory).fromEthNetworkConfig(eq(networkConfig)); + verify(mockControllerBuilderFactory).fromEthNetworkConfig(eq(networkConfig), any()); verify(mockControllerBuilder).synchronizerConfiguration(syncConfigurationCaptor.capture()); assertThat(syncConfigurationCaptor.getValue().getSyncMode()).isEqualTo(SyncMode.FAST); @@ -891,7 +893,7 @@ public class PantheonCommandTest extends CommandTestAbstract { parseCommand("--genesis-file", genesisFile.toString()); - verify(mockControllerBuilderFactory).fromEthNetworkConfig(networkArg.capture()); + verify(mockControllerBuilderFactory).fromEthNetworkConfig(networkArg.capture(), any()); verify(mockControllerBuilder).build(); assertThat(networkArg.getValue().getGenesisConfig()) @@ -927,7 +929,7 @@ public class PantheonCommandTest extends CommandTestAbstract { final ArgumentCaptor networkArg = ArgumentCaptor.forClass(EthNetworkConfig.class); - verify(mockControllerBuilderFactory).fromEthNetworkConfig(networkArg.capture()); + verify(mockControllerBuilderFactory).fromEthNetworkConfig(networkArg.capture(), any()); verify(mockControllerBuilder).build(); assertThat(networkArg.getValue().getGenesisConfig()) @@ -950,7 +952,7 @@ public class PantheonCommandTest extends CommandTestAbstract { final ArgumentCaptor networkArg = ArgumentCaptor.forClass(EthNetworkConfig.class); - verify(mockControllerBuilderFactory).fromEthNetworkConfig(networkArg.capture()); + verify(mockControllerBuilderFactory).fromEthNetworkConfig(networkArg.capture(), any()); verify(mockControllerBuilder).build(); assertThat(networkArg.getValue().getGenesisConfig()) @@ -963,6 +965,22 @@ public class PantheonCommandTest extends CommandTestAbstract { assertThat(commandErrorOutput.toString()).isEmpty(); } + @Test + @SuppressWarnings("unchecked") + public void overrideGenesisConfigFileChange() throws Exception { + final ArgumentCaptor> overrides = ArgumentCaptor.forClass(Map.class); + + parseCommand("--network=dev", "--override-genesis-config=chainId=8675309"); + + verify(mockControllerBuilderFactory).fromEthNetworkConfig(any(), overrides.capture()); + verify(mockControllerBuilder).build(); + assertThat(overrides.getValue()).containsOnlyKeys("chainId"); + assertThat(overrides.getValue()).containsEntry("chainId", "8675309"); + + assertThat(commandOutput.toString()).isEmpty(); + assertThat(commandErrorOutput.toString()).isEmpty(); + } + @Test public void predefinedNetworkIdsMustBeEqualToChainIds() { // check the network id against the one in mainnet genesis config @@ -2344,7 +2362,7 @@ public class PantheonCommandTest extends CommandTestAbstract { final ArgumentCaptor networkArg = ArgumentCaptor.forClass(EthNetworkConfig.class); - verify(mockControllerBuilderFactory).fromEthNetworkConfig(networkArg.capture()); + verify(mockControllerBuilderFactory).fromEthNetworkConfig(networkArg.capture(), any()); verify(mockControllerBuilder).build(); assertThat(networkArg.getValue()).isEqualTo(EthNetworkConfig.getNetworkConfig(DEV)); @@ -2360,7 +2378,7 @@ public class PantheonCommandTest extends CommandTestAbstract { final ArgumentCaptor networkArg = ArgumentCaptor.forClass(EthNetworkConfig.class); - verify(mockControllerBuilderFactory).fromEthNetworkConfig(networkArg.capture()); + verify(mockControllerBuilderFactory).fromEthNetworkConfig(networkArg.capture(), any()); verify(mockControllerBuilder).build(); assertThat(networkArg.getValue()).isEqualTo(EthNetworkConfig.getNetworkConfig(RINKEBY)); @@ -2376,7 +2394,7 @@ public class PantheonCommandTest extends CommandTestAbstract { final ArgumentCaptor networkArg = ArgumentCaptor.forClass(EthNetworkConfig.class); - verify(mockControllerBuilderFactory).fromEthNetworkConfig(networkArg.capture()); + verify(mockControllerBuilderFactory).fromEthNetworkConfig(networkArg.capture(), any()); verify(mockControllerBuilder).build(); assertThat(networkArg.getValue()).isEqualTo(EthNetworkConfig.getNetworkConfig(ROPSTEN)); @@ -2392,7 +2410,7 @@ public class PantheonCommandTest extends CommandTestAbstract { final ArgumentCaptor networkArg = ArgumentCaptor.forClass(EthNetworkConfig.class); - verify(mockControllerBuilderFactory).fromEthNetworkConfig(networkArg.capture()); + verify(mockControllerBuilderFactory).fromEthNetworkConfig(networkArg.capture(), any()); verify(mockControllerBuilder).build(); assertThat(networkArg.getValue()).isEqualTo(EthNetworkConfig.getNetworkConfig(GOERLI)); @@ -2433,7 +2451,7 @@ public class PantheonCommandTest extends CommandTestAbstract { final ArgumentCaptor networkArg = ArgumentCaptor.forClass(EthNetworkConfig.class); - verify(mockControllerBuilderFactory).fromEthNetworkConfig(networkArg.capture()); + verify(mockControllerBuilderFactory).fromEthNetworkConfig(networkArg.capture(), any()); verify(mockControllerBuilder).build(); assertThat(networkArg.getValue().getBootNodes())