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

Commit

Permalink
[PAN-3126] add an override facility for genesis configs (#1915)
Browse files Browse the repository at this point in the history
Add a generalized genesis config override facility for command line use. Only useful for string or integer config values, not object values.

Sample use - enable Istanbul fork on block 99 million
--override-genesis-config=istanbulFork=99000000

Overriding a value with an empty string unsets the value

Sample use - disable Istanbul fork by un-setting istanbulFork:
--override-genesis-config=istanbulFork=
  • Loading branch information
Danno Ferrin authored Sep 9, 2019
1 parent 61eeab1 commit 4ba5c6e
Show file tree
Hide file tree
Showing 12 changed files with 229 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,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;

Expand Down Expand Up @@ -82,9 +84,13 @@ public static GenesisConfigFile fromConfig(final ObjectNode config) {
}

public GenesisConfigOptions getConfigOptions() {
return getConfigOptions(Collections.emptyMap());
}

public GenesisConfigOptions getConfigOptions(final Map<String, String> overrides) {
ObjectNode config =
JsonUtil.getObjectNode(configRoot, "config").orElse(JsonUtil.createEmptyObjectNode());
return new JsonGenesisConfigOptions(config);
return new JsonGenesisConfigOptions(config, overrides);
}

public Stream<GenesisAllocation> streamAllocations() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@
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;
Expand All @@ -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<String, String> 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<String, String> configOverrides) {
this.configRoot = isNull(maybeConfig) ? JsonUtil.createEmptyObjectNode() : maybeConfig;
if (configOverrides != null) {
this.configOverrides.putAll(configOverrides);
}
}

@Override
Expand Down Expand Up @@ -148,17 +159,17 @@ public OptionalLong getIstanbulBlockNumber() {

@Override
public Optional<BigInteger> 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
Expand All @@ -176,9 +187,8 @@ public Map<String, Object> asMap() {
.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(
Expand Down Expand Up @@ -207,7 +217,45 @@ public Map<String, Object> asMap() {
return builder.build();
}

private Optional<String> 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<BigInteger> 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));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@
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;

Expand Down Expand Up @@ -214,6 +216,78 @@ public void acceptComments() {
// 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<String, String> 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<String, String> 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<String, String> 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<String, String> 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 + "\"}");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@
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;

Expand Down Expand Up @@ -650,6 +651,16 @@ void setBannedNodeIds(final List<String> values) {
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<String, String> genesisConfigOverrides =
new TreeMap<>(String.CASE_INSENSITIVE_ORDER);

private EthNetworkConfig ethNetworkConfig;
private JsonRpcConfiguration jsonRpcConfiguration;
private GraphQLConfiguration graphQLConfiguration;
Expand Down Expand Up @@ -932,7 +943,7 @@ public PantheonController<?> buildController() {
public PantheonControllerBuilder<?> getControllerBuilder() {
try {
return controllerBuilderFactory
.fromEthNetworkConfig(updateNetworkConfig(getNetwork()))
.fromEthNetworkConfig(updateNetworkConfig(getNetwork()), genesisConfigOverrides)
.synchronizerConfiguration(buildSyncConfig())
.ethProtocolConfiguration(ethProtocolOptions.toDomainObject())
.rocksDbConfiguration(buildRocksDbConfiguration())
Expand All @@ -946,7 +957,8 @@ public PantheonControllerBuilder<?> getControllerBuilder() {
.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);
}
Expand Down Expand Up @@ -1351,7 +1363,7 @@ private EthNetworkConfig updateNetworkConfig(final NetworkName network) {
final GenesisConfigFile genesisConfigFile = GenesisConfigFile.fromConfig(genesisConfig());
builder.setNetworkId(
genesisConfigFile
.getConfigOptions()
.getConfigOptions(genesisConfigOverrides)
.getChainId()
.orElse(EthNetworkConfig.getNetworkConfig(MAINNET).getNetworkId()));
} catch (final DecodeException e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ public class CliquePantheonControllerBuilder extends PantheonControllerBuilder<C
protected void prepForBuild() {
localAddress = Util.publicKeyToAddress(nodeKeys.getPublicKey());
final CliqueConfigOptions cliqueConfig =
genesisConfig.getConfigOptions().getCliqueConfigOptions();
genesisConfig.getConfigOptions(genesisConfigOverrides).getCliqueConfigOptions();
final long blocksPerEpoch = cliqueConfig.getEpochLength();
secondsBetweenBlocks = cliqueConfig.getBlockPeriodSeconds();

Expand Down Expand Up @@ -121,7 +121,10 @@ protected MiningCoordinator createMiningCoordinator(
@Override
protected ProtocolSchedule<CliqueContext> createProtocolSchedule() {
return CliqueProtocolSchedule.create(
genesisConfig.getConfigOptions(), nodeKeys, privacyParameters, isRevertReasonEnabled);
genesisConfig.getConfigOptions(genesisConfigOverrides),
nodeKeys,
privacyParameters,
isRevertReasonEnabled);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,14 +64,16 @@ protected MiningCoordinator createMiningCoordinator(
@Override
protected ProtocolSchedule<IbftContext> 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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ public class IbftPantheonControllerBuilder extends PantheonControllerBuilder<Ibf

@Override
protected void prepForBuild() {
ibftConfig = genesisConfig.getConfigOptions().getIbft2ConfigOptions();
ibftConfig = genesisConfig.getConfigOptions(genesisConfigOverrides).getIbft2ConfigOptions();
ibftEventQueue = new IbftEventQueue(ibftConfig.getMessageQueueLimit());
}

Expand Down Expand Up @@ -211,7 +211,9 @@ protected MiningCoordinator createMiningCoordinator(
@Override
protected ProtocolSchedule<IbftContext> createProtocolSchedule() {
return IbftProtocolSchedule.create(
genesisConfig.getConfigOptions(), privacyParameters, isRevertReasonEnabled);
genesisConfig.getConfigOptions(genesisConfigOverrides),
privacyParameters,
isRevertReasonEnabled);
}

@Override
Expand All @@ -226,7 +228,8 @@ protected void validateContext(final ProtocolContext<IbftContext> context) {
@Override
protected IbftContext createConsensusContext(
final Blockchain blockchain, final WorldStateArchive worldStateArchive) {
final IbftConfigOptions ibftConfig = genesisConfig.getConfigOptions().getIbft2ConfigOptions();
final IbftConfigOptions ibftConfig =
genesisConfig.getConfigOptions(genesisConfigOverrides).getIbft2ConfigOptions();
final EpochManager epochManager = new EpochManager(ibftConfig.getEpochLength());
return new IbftContext(
new VoteTallyCache(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ protected Void createConsensusContext(
@Override
protected ProtocolSchedule<Void> createProtocolSchedule() {
return MainnetProtocolSchedule.fromConfig(
genesisConfig.getConfigOptions(), privacyParameters, isRevertReasonEnabled);
genesisConfig.getConfigOptions(genesisConfigOverrides),
privacyParameters,
isRevertReasonEnabled);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import tech.pegasys.pantheon.ethereum.p2p.config.SubProtocolConfiguration;

import java.util.Collection;
import java.util.Collections;
import java.util.Map;

public class PantheonController<C> implements java.io.Closeable {
Expand Down Expand Up @@ -129,12 +130,25 @@ public static class Builder {

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<String, String> 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<String, String> genesisConfigOverrides) {
final GenesisConfigOptions configOptions =
genesisConfig.getConfigOptions(genesisConfigOverrides);
final PantheonControllerBuilder<?> builder;

if (configOptions.isEthHash()) {
Expand Down
Loading

0 comments on commit 4ba5c6e

Please sign in to comment.