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

[PAN-3126] add an override facility for genesis configs #1915

Merged
merged 7 commits into from
Sep 9, 2019
Merged
Show file tree
Hide file tree
Changes from all 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 @@ -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);
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we add some tests that use a case-sensitive Map with various casing strategies for the keys to verify that values are replaced in a case-insensitive way?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

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