Add wrapper classes for config section of genesis file (#208)

* Introduce classes to wrap JSON config instead of accessing it directly in multiple places.

* Fix discrepancy in how CliqueProtocolSchedule and CliquePantheonController loaded the block period configuration.
Signed-off-by: Adrian Sutton <adrian.sutton@consensys.net>
pull/2/head
Adrian Sutton 6 years ago committed by GitHub
parent 338879d9a8
commit 0f364d4285
  1. 36
      config/build.gradle
  2. 37
      config/src/main/java/tech/pegasys/pantheon/config/CliqueConfigOptions.java
  3. 98
      config/src/main/java/tech/pegasys/pantheon/config/GenesisConfigOptions.java
  4. 42
      config/src/main/java/tech/pegasys/pantheon/config/IbftConfigOptions.java
  5. 70
      config/src/test/java/tech/pegasys/pantheon/config/CliqueConfigOptionsTest.java
  6. 134
      config/src/test/java/tech/pegasys/pantheon/config/GenesisConfigOptionsTest.java
  7. 88
      config/src/test/java/tech/pegasys/pantheon/config/IbftConfigOptionsTest.java
  8. 1
      consensus/clique/build.gradle
  9. 55
      consensus/clique/src/main/java/tech/pegasys/pantheon/consensus/clique/CliqueProtocolSchedule.java
  10. 13
      consensus/clique/src/test/java/tech/pegasys/pantheon/consensus/clique/CliqueProtocolScheduleTest.java
  11. 7
      consensus/clique/src/test/java/tech/pegasys/pantheon/consensus/clique/blockcreation/CliqueMinerExecutorTest.java
  12. 1
      consensus/ibftlegacy/build.gradle
  13. 28
      consensus/ibftlegacy/src/main/java/tech/pegasys/pantheon/consensus/ibftlegacy/IbftProtocolSchedule.java
  14. 5
      consensus/ibftlegacy/src/test/java/tech/pegasys/pantheon/consensus/ibftlegacy/blockcreation/IbftBlockCreatorTest.java
  15. 1
      ethereum/core/build.gradle
  16. 13
      ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/chain/GenesisConfig.java
  17. 18
      ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/MainnetProtocolSchedule.java
  18. 10
      ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/mainnet/MainnetProtocolScheduleTest.java
  19. 1
      ethereum/jsonrpc/build.gradle
  20. 6
      ethereum/jsonrpc/src/integration-test/java/tech/pegasys/pantheon/ethereum/jsonrpc/BlockchainImporter.java
  21. 1
      pantheon/build.gradle
  22. 11
      pantheon/src/main/java/tech/pegasys/pantheon/controller/CliquePantheonController.java
  23. 15
      pantheon/src/main/java/tech/pegasys/pantheon/controller/IbftPantheonController.java
  24. 13
      pantheon/src/main/java/tech/pegasys/pantheon/controller/PantheonController.java
  25. 8
      pantheon/src/test/resources/ibft_genesis.json
  26. 1
      settings.gradle

@ -0,0 +1,36 @@
/*
* Copyright 2018 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.
*/
apply plugin: 'java-library'
jar {
baseName 'pantheon-config'
manifest {
attributes('Implementation-Title': baseName,
'Implementation-Version': project.version)
}
}
dependencies {
implementation 'com.fasterxml.jackson.core:jackson-databind'
implementation 'io.vertx:vertx-core'
implementation 'org.apache.logging.log4j:log4j-api'
runtime 'org.apache.logging.log4j:log4j-core'
testImplementation project(':testutil')
testImplementation 'org.assertj:assertj-core'
testImplementation 'org.mockito:mockito-core'
testImplementation 'junit:junit'
}

@ -0,0 +1,37 @@
/*
* Copyright 2018 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.
*/
package tech.pegasys.pantheon.config;
import io.vertx.core.json.JsonObject;
public class CliqueConfigOptions {
public static final CliqueConfigOptions DEFAULT = new CliqueConfigOptions(new JsonObject());
private static final long DEFAULT_EPOCH_LENGTH = 30_000;
private static final int DEFAULT_BLOCK_PERIOD_SECONDS = 15;
private final JsonObject cliqueConfigRoot;
public CliqueConfigOptions(final JsonObject cliqueConfigRoot) {
this.cliqueConfigRoot = cliqueConfigRoot;
}
public long getEpochLength() {
return cliqueConfigRoot.getLong("epochLength", DEFAULT_EPOCH_LENGTH);
}
public int getBlockPeriodSeconds() {
return cliqueConfigRoot.getInteger("blockPeriodSeconds", DEFAULT_BLOCK_PERIOD_SECONDS);
}
}

@ -0,0 +1,98 @@
/*
* Copyright 2018 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.
*/
package tech.pegasys.pantheon.config;
import java.util.OptionalInt;
import java.util.OptionalLong;
import io.vertx.core.json.JsonObject;
public class GenesisConfigOptions {
private static final String ETHASH_CONFIG_KEY = "ethash";
private static final String IBFT_CONFIG_KEY = "ibft";
private static final String CLIQUE_CONFIG_KEY = "clique";
private final JsonObject configRoot;
private GenesisConfigOptions(final JsonObject configRoot) {
this.configRoot = configRoot != null ? configRoot : new JsonObject();
}
public static GenesisConfigOptions fromGenesisConfig(final String genesisConfig) {
return fromGenesisConfig(new JsonObject(genesisConfig));
}
public static GenesisConfigOptions fromGenesisConfig(final JsonObject genesisConfig) {
return new GenesisConfigOptions(genesisConfig.getJsonObject("config"));
}
public boolean isEthHash() {
return configRoot.containsKey(ETHASH_CONFIG_KEY);
}
public boolean isIbft() {
return configRoot.containsKey(IBFT_CONFIG_KEY);
}
public boolean isClique() {
return configRoot.containsKey(CLIQUE_CONFIG_KEY);
}
public IbftConfigOptions getIbftConfigOptions() {
return isIbft()
? new IbftConfigOptions(configRoot.getJsonObject(IBFT_CONFIG_KEY))
: IbftConfigOptions.DEFAULT;
}
public CliqueConfigOptions getCliqueConfigOptions() {
return isClique()
? new CliqueConfigOptions(configRoot.getJsonObject(CLIQUE_CONFIG_KEY))
: CliqueConfigOptions.DEFAULT;
}
public OptionalLong getHomesteadBlockNumber() {
return getOptionalLong("homesteadBlock");
}
public OptionalLong getDaoForkBlock() {
return getOptionalLong("daoForkBlock");
}
public OptionalLong getTangerineWhistleBlockNumber() {
return getOptionalLong("eip150Block");
}
public OptionalLong getSpuriousDragonBlockNumber() {
return getOptionalLong("eip158Block");
}
public OptionalLong getByzantiumBlockNumber() {
return getOptionalLong("byzantiumBlock");
}
public OptionalLong getConstantinopleBlockNumber() {
return getOptionalLong("constantinopleBlock");
}
public OptionalInt getChainId() {
return configRoot.containsKey("chainId")
? OptionalInt.of(configRoot.getInteger("chainId"))
: OptionalInt.empty();
}
private OptionalLong getOptionalLong(final String key) {
return configRoot.containsKey(key)
? OptionalLong.of(configRoot.getLong(key))
: OptionalLong.empty();
}
}

@ -0,0 +1,42 @@
/*
* Copyright 2018 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.
*/
package tech.pegasys.pantheon.config;
import io.vertx.core.json.JsonObject;
public class IbftConfigOptions {
public static final IbftConfigOptions DEFAULT = new IbftConfigOptions(new JsonObject());
private static final long DEFAULT_EPOCH_LENGTH = 30_000;
private static final int DEFAULT_BLOCK_PERIOD_SECONDS = 1;
private static final int DEFAULT_ROUND_EXPIRY_MILLISECONDS = 10000;
private final JsonObject ibftConfigRoot;
public IbftConfigOptions(final JsonObject ibftConfigRoot) {
this.ibftConfigRoot = ibftConfigRoot;
}
public long getEpochLength() {
return ibftConfigRoot.getLong("epochLength", DEFAULT_EPOCH_LENGTH);
}
public int getBlockPeriodSeconds() {
return ibftConfigRoot.getInteger("blockPeriodSeconds", DEFAULT_BLOCK_PERIOD_SECONDS);
}
public int getRequestTimeoutMillis() {
return ibftConfigRoot.getInteger("requestTimeout", DEFAULT_ROUND_EXPIRY_MILLISECONDS);
}
}

@ -0,0 +1,70 @@
/*
* Copyright 2018 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.
*/
package tech.pegasys.pantheon.config;
import static java.util.Collections.emptyMap;
import static java.util.Collections.singletonMap;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.Map;
import io.vertx.core.json.JsonObject;
import org.junit.Test;
public class CliqueConfigOptionsTest {
private static final long EXPECTED_DEFAULT_EPOCH_LENGTH = 30_000;
private static final int EXPECTED_DEFAULT_BLOCK_PERIOD = 15;
@Test
public void shouldGetEpochLengthFromConfig() {
final CliqueConfigOptions config = fromConfigOptions(singletonMap("epochLength", 10_000));
assertThat(config.getEpochLength()).isEqualTo(10_000);
}
@Test
public void shouldFallbackToDefaultEpochLength() {
final CliqueConfigOptions config = fromConfigOptions(emptyMap());
assertThat(config.getEpochLength()).isEqualTo(EXPECTED_DEFAULT_EPOCH_LENGTH);
}
@Test
public void shouldGetDefaultEpochLengthFromDefaultConfig() {
assertThat(CliqueConfigOptions.DEFAULT.getEpochLength())
.isEqualTo(EXPECTED_DEFAULT_EPOCH_LENGTH);
}
@Test
public void shouldGetBlockPeriodFromConfig() {
final CliqueConfigOptions config = fromConfigOptions(singletonMap("blockPeriodSeconds", 5));
assertThat(config.getBlockPeriodSeconds()).isEqualTo(5);
}
@Test
public void shouldFallbackToDefaultBlockPeriod() {
final CliqueConfigOptions config = fromConfigOptions(emptyMap());
assertThat(config.getBlockPeriodSeconds()).isEqualTo(EXPECTED_DEFAULT_BLOCK_PERIOD);
}
@Test
public void shouldGetDefaultBlockPeriodFromDefaultConfig() {
assertThat(CliqueConfigOptions.DEFAULT.getBlockPeriodSeconds())
.isEqualTo(EXPECTED_DEFAULT_BLOCK_PERIOD);
}
private CliqueConfigOptions fromConfigOptions(final Map<String, Object> cliqueConfigOptions) {
return GenesisConfigOptions.fromGenesisConfig(
new JsonObject(singletonMap("config", singletonMap("clique", cliqueConfigOptions))))
.getCliqueConfigOptions();
}
}

@ -0,0 +1,134 @@
/*
* Copyright 2018 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.
*/
package tech.pegasys.pantheon.config;
import static java.util.Collections.emptyMap;
import static java.util.Collections.singletonMap;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.Collections;
import java.util.Map;
import io.vertx.core.json.JsonObject;
import org.junit.Test;
public class GenesisConfigOptionsTest {
@Test
public void shouldUseEthHashWhenEthHashInConfig() {
final GenesisConfigOptions config = fromConfigOptions(singletonMap("ethash", emptyMap()));
assertThat(config.isEthHash()).isTrue();
}
@Test
public void shouldNotUseEthHashIfEthHashNotPresent() {
final GenesisConfigOptions config = fromConfigOptions(emptyMap());
assertThat(config.isEthHash()).isFalse();
}
@Test
public void shouldUseIbftWhenIbftInConfig() {
final GenesisConfigOptions config = fromConfigOptions(singletonMap("ibft", emptyMap()));
assertThat(config.isIbft()).isTrue();
assertThat(config.getIbftConfigOptions()).isNotSameAs(IbftConfigOptions.DEFAULT);
}
@Test
public void shouldNotUseIbftIfIbftNotPresent() {
final GenesisConfigOptions config = fromConfigOptions(emptyMap());
assertThat(config.isIbft()).isFalse();
assertThat(config.getIbftConfigOptions()).isSameAs(IbftConfigOptions.DEFAULT);
}
@Test
public void shouldUseCliqueWhenCliqueInConfig() {
final GenesisConfigOptions config = fromConfigOptions(singletonMap("clique", emptyMap()));
assertThat(config.isClique()).isTrue();
assertThat(config.getCliqueConfigOptions()).isNotSameAs(CliqueConfigOptions.DEFAULT);
}
@Test
public void shouldNotUseCliqueIfCliqueNotPresent() {
final GenesisConfigOptions config = fromConfigOptions(emptyMap());
assertThat(config.isClique()).isFalse();
assertThat(config.getCliqueConfigOptions()).isSameAs(CliqueConfigOptions.DEFAULT);
}
@Test
public void shouldGetHomesteadBlockNumber() {
final GenesisConfigOptions config = fromConfigOptions(singletonMap("homesteadBlock", 1000));
assertThat(config.getHomesteadBlockNumber()).hasValue(1000);
}
@Test
public void shouldGetDaoForkBlockNumber() {
final GenesisConfigOptions config = fromConfigOptions(singletonMap("daoForkBlock", 1000));
assertThat(config.getDaoForkBlock()).hasValue(1000);
}
@Test
public void shouldGetTangerineWhistleBlockNumber() {
final GenesisConfigOptions config = fromConfigOptions(singletonMap("eip150Block", 1000));
assertThat(config.getTangerineWhistleBlockNumber()).hasValue(1000);
}
@Test
public void shouldGetSpuriousDragonBlockNumber() {
final GenesisConfigOptions config = fromConfigOptions(singletonMap("eip158Block", 1000));
assertThat(config.getSpuriousDragonBlockNumber()).hasValue(1000);
}
@Test
public void shouldGetByzantiumBlockNumber() {
final GenesisConfigOptions config = fromConfigOptions(singletonMap("byzantiumBlock", 1000));
assertThat(config.getByzantiumBlockNumber()).hasValue(1000);
}
@Test
public void shouldGetConstantinopleBlockNumber() {
final GenesisConfigOptions config =
fromConfigOptions(singletonMap("constantinopleBlock", 1000));
assertThat(config.getConstantinopleBlockNumber()).hasValue(1000);
}
@Test
public void shouldNotReturnEmptyOptionalWhenBlockNumberNotSpecified() {
final GenesisConfigOptions config = fromConfigOptions(emptyMap());
assertThat(config.getHomesteadBlockNumber()).isEmpty();
assertThat(config.getDaoForkBlock()).isEmpty();
assertThat(config.getTangerineWhistleBlockNumber()).isEmpty();
assertThat(config.getSpuriousDragonBlockNumber()).isEmpty();
assertThat(config.getByzantiumBlockNumber()).isEmpty();
assertThat(config.getConstantinopleBlockNumber()).isEmpty();
}
@Test
public void shouldGetChainIdWhenSpecified() {
final GenesisConfigOptions config = fromConfigOptions(singletonMap("chainId", 32));
assertThat(config.getChainId()).hasValue(32);
}
@Test
public void shouldSupportEmptyGenesisConfig() {
final GenesisConfigOptions config = GenesisConfigOptions.fromGenesisConfig("{}");
assertThat(config.isEthHash()).isFalse();
assertThat(config.isIbft()).isFalse();
assertThat(config.isClique()).isFalse();
assertThat(config.getHomesteadBlockNumber()).isEmpty();
}
private GenesisConfigOptions fromConfigOptions(final Map<String, Object> options) {
return GenesisConfigOptions.fromGenesisConfig(
new JsonObject(Collections.singletonMap("config", options)));
}
}

@ -0,0 +1,88 @@
/*
* Copyright 2018 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.
*/
package tech.pegasys.pantheon.config;
import static java.util.Collections.emptyMap;
import static java.util.Collections.singletonMap;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.Map;
import io.vertx.core.json.JsonObject;
import org.junit.Test;
public class IbftConfigOptionsTest {
private static final int EXPECTED_DEFAULT_EPOCH_LENGTH = 30_000;
private static final int EXPECTED_DEFAULT_BLOCK_PERIOD = 1;
private static final int EXPECTED_DEFAULT_REQUEST_TIMEOUT = 10_000;
@Test
public void shouldGetEpochLengthFromConfig() {
final IbftConfigOptions config = fromConfigOptions(singletonMap("epochLength", 10_000));
assertThat(config.getEpochLength()).isEqualTo(10_000);
}
@Test
public void shouldFallbackToDefaultEpochLength() {
final IbftConfigOptions config = fromConfigOptions(emptyMap());
assertThat(config.getEpochLength()).isEqualTo(EXPECTED_DEFAULT_EPOCH_LENGTH);
}
@Test
public void shouldGetDefaultEpochLengthFromDefaultConfig() {
assertThat(IbftConfigOptions.DEFAULT.getEpochLength()).isEqualTo(EXPECTED_DEFAULT_EPOCH_LENGTH);
}
@Test
public void shouldGetBlockPeriodFromConfig() {
final IbftConfigOptions config = fromConfigOptions(singletonMap("blockPeriodSeconds", 5));
assertThat(config.getBlockPeriodSeconds()).isEqualTo(5);
}
@Test
public void shouldFallbackToDefaultBlockPeriod() {
final IbftConfigOptions config = fromConfigOptions(emptyMap());
assertThat(config.getBlockPeriodSeconds()).isEqualTo(EXPECTED_DEFAULT_BLOCK_PERIOD);
}
@Test
public void shouldGetDefaultBlockPeriodFromDefaultConfig() {
assertThat(IbftConfigOptions.DEFAULT.getBlockPeriodSeconds())
.isEqualTo(EXPECTED_DEFAULT_BLOCK_PERIOD);
}
@Test
public void shouldGetRequestTimeoutFromConfig() {
final IbftConfigOptions config = fromConfigOptions(singletonMap("requestTimeout", 5));
assertThat(config.getRequestTimeoutMillis()).isEqualTo(5);
}
@Test
public void shouldFallbackToDefaultRequestTimeout() {
final IbftConfigOptions config = fromConfigOptions(emptyMap());
assertThat(config.getRequestTimeoutMillis()).isEqualTo(EXPECTED_DEFAULT_REQUEST_TIMEOUT);
}
@Test
public void shouldGetDefaultRequestTimeoutFromDefaultConfig() {
assertThat(IbftConfigOptions.DEFAULT.getRequestTimeoutMillis())
.isEqualTo(EXPECTED_DEFAULT_REQUEST_TIMEOUT);
}
private IbftConfigOptions fromConfigOptions(final Map<String, Object> ibftConfigOptions) {
return GenesisConfigOptions.fromGenesisConfig(
new JsonObject(singletonMap("config", singletonMap("ibft", ibftConfigOptions))))
.getIbftConfigOptions();
}
}

@ -24,6 +24,7 @@ jar {
repositories { mavenCentral() }
dependencies {
implementation project(':config')
implementation project(':crypto')
implementation project(':ethereum:core')
implementation project(':ethereum:blockcreation')

@ -12,34 +12,26 @@
*/
package tech.pegasys.pantheon.consensus.clique;
import tech.pegasys.pantheon.config.CliqueConfigOptions;
import tech.pegasys.pantheon.config.GenesisConfigOptions;
import tech.pegasys.pantheon.crypto.SECP256K1.KeyPair;
import tech.pegasys.pantheon.ethereum.core.Util;
import tech.pegasys.pantheon.ethereum.mainnet.MutableProtocolSchedule;
import tech.pegasys.pantheon.ethereum.mainnet.ProtocolSchedule;
import java.util.Optional;
import io.vertx.core.json.JsonObject;
/** Defines the protocol behaviours for a blockchain using Clique. */
public class CliqueProtocolSchedule extends MutableProtocolSchedule<CliqueContext> {
private static final long DEFAULT_EPOCH_LENGTH = 30_000;
private static final int DEFAULT_BLOCK_PERIOD_SECONDS = 1;
private static final int DEFAULT_CHAIN_ID = 4;
public static ProtocolSchedule<CliqueContext> create(
final JsonObject config, final KeyPair nodeKeys) {
final GenesisConfigOptions config, final KeyPair nodeKeys) {
// Get Config Data
final Optional<JsonObject> cliqueConfig = Optional.ofNullable(config.getJsonObject("clique"));
final long epochLength =
cliqueConfig.map(cc -> cc.getLong("epochLength")).orElse(DEFAULT_EPOCH_LENGTH);
final long blockPeriod =
cliqueConfig
.map(cc -> cc.getInteger("blockPeriodSeconds"))
.orElse(DEFAULT_BLOCK_PERIOD_SECONDS);
final int chainId = config.getInteger("chainId", DEFAULT_CHAIN_ID);
final CliqueConfigOptions cliqueConfig = config.getCliqueConfigOptions();
final long epochLength = cliqueConfig.getEpochLength();
final long blockPeriod = cliqueConfig.getBlockPeriodSeconds();
final int chainId = config.getChainId().orElse(DEFAULT_CHAIN_ID);
final MutableProtocolSchedule<CliqueContext> protocolSchedule = new CliqueProtocolSchedule();
@ -54,25 +46,20 @@ public class CliqueProtocolSchedule extends MutableProtocolSchedule<CliqueContex
protocolSchedule.putMilestone(0, specs.frontier());
final Long homesteadBlockNumber = config.getLong("homesteadBlock");
if (homesteadBlockNumber != null) {
protocolSchedule.putMilestone(homesteadBlockNumber, specs.homestead());
}
final Long tangerineWhistleBlockNumber = config.getLong("eip150Block");
if (tangerineWhistleBlockNumber != null) {
protocolSchedule.putMilestone(tangerineWhistleBlockNumber, specs.tangerineWhistle());
}
final Long spuriousDragonBlockNumber = config.getLong("eip158Block");
if (spuriousDragonBlockNumber != null) {
protocolSchedule.putMilestone(spuriousDragonBlockNumber, specs.spuriousDragon());
}
final Long byzantiumBlockNumber = config.getLong("byzantiumBlock");
if (byzantiumBlockNumber != null) {
protocolSchedule.putMilestone(byzantiumBlockNumber, specs.byzantium());
}
config
.getHomesteadBlockNumber()
.ifPresent(blockNumber -> protocolSchedule.putMilestone(blockNumber, specs.homestead()));
config
.getTangerineWhistleBlockNumber()
.ifPresent(
blockNumber -> protocolSchedule.putMilestone(blockNumber, specs.tangerineWhistle()));
config
.getSpuriousDragonBlockNumber()
.ifPresent(
blockNumber -> protocolSchedule.putMilestone(blockNumber, specs.spuriousDragon()));
config
.getByzantiumBlockNumber()
.ifPresent(blockNumber -> protocolSchedule.putMilestone(blockNumber, specs.byzantium()));
return protocolSchedule;
}

@ -14,11 +14,11 @@ package tech.pegasys.pantheon.consensus.clique;
import static org.assertj.core.api.Java6Assertions.assertThat;
import tech.pegasys.pantheon.config.GenesisConfigOptions;
import tech.pegasys.pantheon.crypto.SECP256K1.KeyPair;
import tech.pegasys.pantheon.ethereum.mainnet.ProtocolSchedule;
import tech.pegasys.pantheon.ethereum.mainnet.ProtocolSpec;
import io.vertx.core.json.JsonObject;
import org.junit.Test;
public class CliqueProtocolScheduleTest {
@ -26,17 +26,18 @@ public class CliqueProtocolScheduleTest {
@Test
public void protocolSpecsAreCreatedAtBlockDefinedInJson() {
final String jsonInput =
"{\"chainId\": 4,\n"
"{\"config\": "
+ "{\"chainId\": 4,\n"
+ "\"homesteadBlock\": 1,\n"
+ "\"eip150Block\": 2,\n"
+ "\"eip155Block\": 3,\n"
+ "\"eip158Block\": 3,\n"
+ "\"byzantiumBlock\": 1035301}";
final JsonObject jsonObject = new JsonObject(jsonInput);
+ "\"byzantiumBlock\": 1035301}"
+ "}";
final GenesisConfigOptions config = GenesisConfigOptions.fromGenesisConfig(jsonInput);
final ProtocolSchedule<CliqueContext> protocolSchedule =
CliqueProtocolSchedule.create(jsonObject, KeyPair.generate());
CliqueProtocolSchedule.create(config, KeyPair.generate());
final ProtocolSpec<CliqueContext> homesteadSpec = protocolSchedule.getByBlockNumber(1);
final ProtocolSpec<CliqueContext> tangerineWhistleSpec = protocolSchedule.getByBlockNumber(2);

@ -17,6 +17,7 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import tech.pegasys.pantheon.config.GenesisConfigOptions;
import tech.pegasys.pantheon.consensus.clique.CliqueContext;
import tech.pegasys.pantheon.consensus.clique.CliqueExtraData;
import tech.pegasys.pantheon.consensus.clique.CliqueProtocolSchedule;
@ -47,6 +48,8 @@ import org.junit.Test;
public class CliqueMinerExecutorTest {
private static final GenesisConfigOptions GENESIS_CONFIG_OPTIONS =
GenesisConfigOptions.fromGenesisConfig(new JsonObject());
private final KeyPair proposerKeyPair = KeyPair.generate();
private Address localAddress;
private final List<Address> validatorList = Lists.newArrayList();
@ -81,7 +84,7 @@ public class CliqueMinerExecutorTest {
new CliqueMinerExecutor(
cliqueProtocolContext,
Executors.newSingleThreadExecutor(),
CliqueProtocolSchedule.create(new JsonObject(), proposerKeyPair),
CliqueProtocolSchedule.create(GENESIS_CONFIG_OPTIONS, proposerKeyPair),
new PendingTransactions(1),
proposerKeyPair,
new MiningParameters(AddressHelpers.ofValue(1), Wei.ZERO, wrappedVanityData, false),
@ -111,7 +114,7 @@ public class CliqueMinerExecutorTest {
new CliqueMinerExecutor(
cliqueProtocolContext,
Executors.newSingleThreadExecutor(),
CliqueProtocolSchedule.create(new JsonObject(), proposerKeyPair),
CliqueProtocolSchedule.create(GENESIS_CONFIG_OPTIONS, proposerKeyPair),
new PendingTransactions(1),
proposerKeyPair,
new MiningParameters(AddressHelpers.ofValue(1), Wei.ZERO, wrappedVanityData, false),

@ -11,6 +11,7 @@ jar {
dependencies {
implementation project(':consensus:common')
implementation project(':consensus:ibft')
implementation project(':config')
implementation project(':crypto')
implementation project(':ethereum:core')
implementation project(':ethereum:blockcreation')

@ -12,29 +12,23 @@
*/
package tech.pegasys.pantheon.consensus.ibftlegacy;
import tech.pegasys.pantheon.config.GenesisConfigOptions;
import tech.pegasys.pantheon.config.IbftConfigOptions;
import tech.pegasys.pantheon.consensus.ibft.IbftContext;
import tech.pegasys.pantheon.ethereum.mainnet.MutableProtocolSchedule;
import tech.pegasys.pantheon.ethereum.mainnet.ProtocolSchedule;
import java.util.Optional;
import io.vertx.core.json.JsonObject;
/** Defines the protocol behaviours for a blockchain using IBFT. */
public class IbftProtocolSchedule {
private static final long DEFAULT_EPOCH_LENGTH = 30_000;
private static final int DEFAULT_BLOCK_PERIOD_SECONDS = 1;
private static final int DEFAULT_CHAIN_ID = 1;
public static ProtocolSchedule<IbftContext> create(final JsonObject config) {
final long spuriousDragonBlock = config.getLong("spuriousDragonBlock", 0L);
final Optional<JsonObject> ibftConfig = Optional.ofNullable(config.getJsonObject("ibft"));
final int chainId = config.getInteger("chainId", 1);
final long epochLength = getEpochLength(ibftConfig);
final long blockPeriod =
ibftConfig
.map(iC -> iC.getInteger("blockPeriodSeconds"))
.orElse(DEFAULT_BLOCK_PERIOD_SECONDS);
public static ProtocolSchedule<IbftContext> create(final GenesisConfigOptions config) {
final long spuriousDragonBlock = config.getSpuriousDragonBlockNumber().orElse(0);
final IbftConfigOptions ibftConfig = config.getIbftConfigOptions();
final int chainId = config.getChainId().orElse(DEFAULT_CHAIN_ID);
final long epochLength = ibftConfig.getEpochLength();
final long blockPeriod = ibftConfig.getBlockPeriodSeconds();
final MutableProtocolSchedule<IbftContext> protocolSchedule = new MutableProtocolSchedule<>();
protocolSchedule.putMilestone(
@ -42,8 +36,4 @@ public class IbftProtocolSchedule {
IbftProtocolSpecs.spuriousDragon(blockPeriod, epochLength, chainId, protocolSchedule));
return protocolSchedule;
}
public static long getEpochLength(final Optional<JsonObject> ibftConfig) {
return ibftConfig.map(conf -> conf.getLong("epochLength")).orElse(DEFAULT_EPOCH_LENGTH);
}
}

@ -18,6 +18,7 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static tech.pegasys.pantheon.ethereum.core.InMemoryTestFixture.createInMemoryWorldStateArchive;
import tech.pegasys.pantheon.config.GenesisConfigOptions;
import tech.pegasys.pantheon.consensus.common.VoteProposer;
import tech.pegasys.pantheon.consensus.common.VoteTally;
import tech.pegasys.pantheon.consensus.ibft.IbftContext;
@ -45,7 +46,6 @@ import java.util.List;
import java.util.Optional;
import com.google.common.collect.Lists;
import io.vertx.core.json.JsonObject;
import org.junit.Test;
public class IbftBlockCreatorTest {
@ -77,7 +77,8 @@ public class IbftBlockCreatorTest {
final VoteTally voteTally = new VoteTally(initialValidatorList);
final ProtocolSchedule<IbftContext> protocolSchedule =
IbftProtocolSchedule.create(new JsonObject("{\"spuriousDragonBlock\":0}"));
IbftProtocolSchedule.create(
GenesisConfigOptions.fromGenesisConfig("{\"config\": {\"spuriousDragonBlock\":0}}"));
final ProtocolContext<IbftContext> protContext =
new ProtocolContext<>(
blockchain,

@ -22,6 +22,7 @@ jar {
}
dependencies {
implementation project(':config')
implementation project(':crypto')
implementation project(':ethereum:rlp')
implementation project(':ethereum:trie')

@ -12,6 +12,7 @@
*/
package tech.pegasys.pantheon.ethereum.chain;
import tech.pegasys.pantheon.config.GenesisConfigOptions;
import tech.pegasys.pantheon.ethereum.core.Address;
import tech.pegasys.pantheon.ethereum.core.Block;
import tech.pegasys.pantheon.ethereum.core.BlockBody;
@ -75,8 +76,8 @@ public final class GenesisConfig<C> {
final JsonObject config =
new JsonObject(
Resources.toString(Resources.getResource(MAINNET_FILE), StandardCharsets.UTF_8));
return GenesisConfig.fromConfig(
config, MainnetProtocolSchedule.fromConfig(config.getJsonObject("config")));
final GenesisConfigOptions configOptions = GenesisConfigOptions.fromGenesisConfig(config);
return GenesisConfig.fromConfig(config, MainnetProtocolSchedule.fromConfig(configOptions));
} catch (final IOException ex) {
throw new IllegalStateException(ex);
}
@ -125,10 +126,10 @@ public final class GenesisConfig<C> {
new Block(
buildHeader(definition, calculateGenesisStateHash(genesisAccounts), protocolSchedule),
BODY);
final Map<String, Object> config =
(Map<String, Object>) definition.getOrDefault("config", Collections.emptyMap());
final int chainId = (int) config.getOrDefault("chainId", 1);
final int chainId =
GenesisConfigOptions.fromGenesisConfig(jsonConfig)
.getChainId()
.orElse(MainnetProtocolSchedule.DEFAULT_CHAIN_ID);
return new GenesisConfig<>(block, chainId, protocolSchedule, genesisAccounts);
}

@ -12,6 +12,8 @@
*/
package tech.pegasys.pantheon.ethereum.mainnet;
import tech.pegasys.pantheon.config.GenesisConfigOptions;
import io.vertx.core.json.JsonObject;
/** Provides {@link ProtocolSpec} lookups for mainnet hard forks. */
@ -92,19 +94,19 @@ public class MainnetProtocolSchedule {
* points
* @return A configured mainnet protocol schedule
*/
public static ProtocolSchedule<Void> fromConfig(final JsonObject config) {
public static ProtocolSchedule<Void> fromConfig(final GenesisConfigOptions config) {
final long homesteadBlockNumber =
config.getLong("homesteadBlock", DEFAULT_HOMESTEAD_BLOCK_NUMBER);
final long daoBlockNumber = config.getLong("daoForkBlock", DEFAULT_DAO_BLOCK_NUMBER);
config.getHomesteadBlockNumber().orElse(DEFAULT_HOMESTEAD_BLOCK_NUMBER);
final long daoBlockNumber = config.getDaoForkBlock().orElse(DEFAULT_DAO_BLOCK_NUMBER);
final long tangerineWhistleBlockNumber =
config.getLong("eip150Block", DEFAULT_TANGERINE_WHISTLE_BLOCK_NUMBER);
config.getTangerineWhistleBlockNumber().orElse(DEFAULT_TANGERINE_WHISTLE_BLOCK_NUMBER);
final long spuriousDragonBlockNumber =
config.getLong("eip158Block", DEFAULT_SPURIOUS_DRAGON_BLOCK_NUMBER);
config.getSpuriousDragonBlockNumber().orElse(DEFAULT_SPURIOUS_DRAGON_BLOCK_NUMBER);
final long byzantiumBlockNumber =
config.getLong("byzantiumBlock", DEFAULT_BYZANTIUM_BLOCK_NUMBER);
config.getByzantiumBlockNumber().orElse(DEFAULT_BYZANTIUM_BLOCK_NUMBER);
final long constantinopleBlockNumber =
config.getLong("constantinopleBlock", DEFAULT_CONSTANTINOPLE_BLOCK_NUMBER);
final int chainId = config.getInteger("chainId", DEFAULT_CHAIN_ID);
config.getConstantinopleBlockNumber().orElse(DEFAULT_CONSTANTINOPLE_BLOCK_NUMBER);
final int chainId = config.getChainId().orElse(DEFAULT_CHAIN_ID);
return create(
homesteadBlockNumber,
daoBlockNumber,

@ -12,6 +12,8 @@
*/
package tech.pegasys.pantheon.ethereum.mainnet;
import tech.pegasys.pantheon.config.GenesisConfigOptions;
import io.vertx.core.json.JsonObject;
import org.assertj.core.api.Assertions;
import org.junit.Test;
@ -52,7 +54,8 @@ public class MainnetProtocolScheduleTest {
@Test
public void shouldReturnDefaultProtocolSpecsWhenEmptyJsonConfigIsUsed() {
final JsonObject json = new JsonObject("{}");
final ProtocolSchedule<Void> sched = MainnetProtocolSchedule.fromConfig(json);
final ProtocolSchedule<Void> sched =
MainnetProtocolSchedule.fromConfig(GenesisConfigOptions.fromGenesisConfig(json));
Assertions.assertThat(sched.getByBlockNumber(1L).getName()).isEqualTo("Frontier");
Assertions.assertThat(sched.getByBlockNumber(1_150_000L).getName()).isEqualTo("Homestead");
Assertions.assertThat(sched.getByBlockNumber(1_920_000L).getName())
@ -71,8 +74,9 @@ public class MainnetProtocolScheduleTest {
public void createFromConfigWithSettings() {
final JsonObject json =
new JsonObject(
"{\"homesteadBlock\": 2, \"daoForkBlock\": 3, \"eip150Block\": 14, \"eip158Block\": 15, \"byzantiumBlock\": 16, \"constantinopleBlock\": 18, \"chainId\":1234}");
final ProtocolSchedule<Void> sched = MainnetProtocolSchedule.fromConfig(json);
"{\"config\": {\"homesteadBlock\": 2, \"daoForkBlock\": 3, \"eip150Block\": 14, \"eip158Block\": 15, \"byzantiumBlock\": 16, \"constantinopleBlock\": 18, \"chainId\":1234}}");
final ProtocolSchedule<Void> sched =
MainnetProtocolSchedule.fromConfig(GenesisConfigOptions.fromGenesisConfig(json));
Assertions.assertThat(sched.getByBlockNumber(1).getName()).isEqualTo("Frontier");
Assertions.assertThat(sched.getByBlockNumber(2).getName()).isEqualTo("Homestead");
Assertions.assertThat(sched.getByBlockNumber(3).getName()).isEqualTo("DaoRecoveryInit");

@ -45,6 +45,7 @@ dependencies {
testImplementation 'io.vertx:vertx-codegen'
testImplementation 'io.vertx:vertx-unit'
integrationTestImplementation project(':config')
integrationTestImplementation project(path: ':ethereum:core', configuration: 'testSupportArtifacts')
integrationTestImplementation project(':services:kvstore')

@ -12,6 +12,7 @@
*/
package tech.pegasys.pantheon.ethereum.jsonrpc;
import tech.pegasys.pantheon.config.GenesisConfigOptions;
import tech.pegasys.pantheon.ethereum.chain.GenesisConfig;
import tech.pegasys.pantheon.ethereum.core.Block;
import tech.pegasys.pantheon.ethereum.core.BlockHeader;
@ -25,8 +26,6 @@ import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import io.vertx.core.json.JsonObject;
/** Creates a block chain from a genesis and a blocks files. */
public class BlockchainImporter {
@ -39,7 +38,8 @@ public class BlockchainImporter {
private final Block genesisBlock;
public BlockchainImporter(final URL blocksUrl, final String genesisJson) throws Exception {
protocolSchedule = MainnetProtocolSchedule.fromConfig(new JsonObject(genesisJson));
protocolSchedule =
MainnetProtocolSchedule.fromConfig(GenesisConfigOptions.fromGenesisConfig(genesisJson));
blocks = new ArrayList<>();
try (final RawBlockIterator iterator =

@ -22,6 +22,7 @@ jar {
}
dependencies {
implementation project(':config')
implementation project(':crypto')
implementation project(':consensus:common')
implementation project(':consensus:clique')

@ -14,6 +14,7 @@ package tech.pegasys.pantheon.controller;
import static org.apache.logging.log4j.LogManager.getLogger;
import tech.pegasys.pantheon.config.CliqueConfigOptions;
import tech.pegasys.pantheon.consensus.clique.CliqueContext;
import tech.pegasys.pantheon.consensus.clique.CliqueVoteTallyUpdater;
import tech.pegasys.pantheon.consensus.clique.VoteTallyCache;
@ -59,7 +60,6 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import io.vertx.core.json.JsonObject;
import org.apache.logging.log4j.Logger;
public class CliquePantheonController implements PantheonController<CliqueContext> {
@ -73,8 +73,6 @@ public class CliquePantheonController implements PantheonController<CliqueContex
private final TransactionPool transactionPool;
private final Runnable closer;
private static final long EPOCH_LENGTH_DEFAULT = 30_000L;
private static final long SECONDS_BETWEEN_BLOCKS_DEFAULT = 15L;
private final MiningCoordinator miningCoordinator;
CliquePantheonController(
@ -102,13 +100,12 @@ public class CliquePantheonController implements PantheonController<CliqueContex
final GenesisConfig<CliqueContext> genesisConfig,
final SynchronizerConfiguration taintedSyncConfig,
final MiningParameters miningParams,
final JsonObject cliqueConfig,
final CliqueConfigOptions cliqueConfig,
final int networkId,
final KeyPair nodeKeys)
throws IOException {
final long blocksPerEpoch = cliqueConfig.getLong("epoch", EPOCH_LENGTH_DEFAULT);
final long secondsBetweenBlocks =
cliqueConfig.getLong("period", SECONDS_BETWEEN_BLOCKS_DEFAULT);
final long blocksPerEpoch = cliqueConfig.getEpochLength();
final long secondsBetweenBlocks = cliqueConfig.getBlockPeriodSeconds();
final EpochManager epochManger = new EpochManager(blocksPerEpoch);
final KeyValueStorage kv =

@ -14,6 +14,7 @@ package tech.pegasys.pantheon.controller;
import static org.apache.logging.log4j.LogManager.getLogger;
import tech.pegasys.pantheon.config.IbftConfigOptions;
import tech.pegasys.pantheon.consensus.common.EpochManager;
import tech.pegasys.pantheon.consensus.common.VoteProposer;
import tech.pegasys.pantheon.consensus.common.VoteTally;
@ -25,7 +26,6 @@ import tech.pegasys.pantheon.consensus.ibft.IbftStateMachine;
import tech.pegasys.pantheon.consensus.ibft.network.IbftNetworkPeers;
import tech.pegasys.pantheon.consensus.ibft.protocol.IbftProtocolManager;
import tech.pegasys.pantheon.consensus.ibft.protocol.IbftSubProtocol;
import tech.pegasys.pantheon.consensus.ibftlegacy.IbftProtocolSchedule;
import tech.pegasys.pantheon.consensus.ibftlegacy.IbftVoteTallyUpdater;
import tech.pegasys.pantheon.consensus.ibftlegacy.protocol.Istanbul64Protocol;
import tech.pegasys.pantheon.consensus.ibftlegacy.protocol.Istanbul64ProtocolManager;
@ -60,17 +60,14 @@ import tech.pegasys.pantheon.services.kvstore.RocksDbKeyValueStorage;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Optional;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import io.vertx.core.json.JsonObject;
import org.apache.logging.log4j.Logger;
public class IbftPantheonController implements PantheonController<IbftContext> {
private static final int DEFAULT_ROUND_EXPIRY_MILLISECONDS = 10000;
private static final Logger LOG = getLogger();
private final GenesisConfig<IbftContext> genesisConfig;
private final ProtocolContext<IbftContext> context;
@ -112,7 +109,7 @@ public class IbftPantheonController implements PantheonController<IbftContext> {
final GenesisConfig<IbftContext> genesisConfig,
final SynchronizerConfiguration taintedSyncConfig,
final boolean ottomanTestnetOperation,
final JsonObject ibftConfig,
final IbftConfigOptions ibftConfig,
final int networkId,
final KeyPair nodeKeys)
throws IOException {
@ -132,8 +129,7 @@ public class IbftPantheonController implements PantheonController<IbftContext> {
final WorldStateArchive worldStateArchive = new WorldStateArchive(worldStateStorage);
genesisConfig.writeStateTo(worldStateArchive.getMutable(Hash.EMPTY_TRIE_HASH));
final EpochManager epochManager =
new EpochManager(IbftProtocolSchedule.getEpochLength(Optional.of(ibftConfig)));
final EpochManager epochManager = new EpochManager(ibftConfig.getEpochLength());
final VoteTally voteTally =
new IbftVoteTallyUpdater(epochManager).buildVoteTallyFromBlockchain(blockchain);
@ -176,10 +172,7 @@ public class IbftPantheonController implements PantheonController<IbftContext> {
final IbftStateMachine ibftStateMachine = new IbftStateMachine();
final IbftProcessor ibftProcessor =
new IbftProcessor(
ibftEventQueue,
ibftConfig.getInteger("requestTimeout", DEFAULT_ROUND_EXPIRY_MILLISECONDS),
ibftStateMachine);
new IbftProcessor(ibftEventQueue, ibftConfig.getRequestTimeoutMillis(), ibftStateMachine);
final ExecutorService processorExecutor = Executors.newSingleThreadExecutor();
processorExecutor.submit(ibftProcessor);

@ -12,6 +12,7 @@
*/
package tech.pegasys.pantheon.controller;
import tech.pegasys.pantheon.config.GenesisConfigOptions;
import tech.pegasys.pantheon.consensus.clique.CliqueProtocolSchedule;
import tech.pegasys.pantheon.consensus.ibftlegacy.IbftProtocolSchedule;
import tech.pegasys.pantheon.crypto.SECP256K1.KeyPair;
@ -47,31 +48,31 @@ public interface PantheonController<C> extends Closeable {
throws IOException {
final JsonObject config = new JsonObject(configContents);
final JsonObject configOptions = config.getJsonObject("config");
final GenesisConfigOptions configOptions = GenesisConfigOptions.fromGenesisConfig(config);
if (configOptions.containsKey("ethash")) {
if (configOptions.isEthHash()) {
return MainnetPantheonController.init(
pantheonHome,
GenesisConfig.fromConfig(config, MainnetProtocolSchedule.fromConfig(configOptions)),
syncConfig,
miningParameters,
nodeKeys);
} else if (configOptions.containsKey("ibft")) {
} else if (configOptions.isIbft()) {
return IbftPantheonController.init(
pantheonHome,
GenesisConfig.fromConfig(config, IbftProtocolSchedule.create(configOptions)),
syncConfig,
ottomanTestnetOperation,
configOptions.getJsonObject("ibft"),
configOptions.getIbftConfigOptions(),
networkId,
nodeKeys);
} else if (configOptions.containsKey("clique")) {
} else if (configOptions.isClique()) {
return CliquePantheonController.init(
pantheonHome,
GenesisConfig.fromConfig(config, CliqueProtocolSchedule.create(configOptions, nodeKeys)),
syncConfig,
miningParameters,
configOptions.getJsonObject("clique"),
configOptions.getCliqueConfigOptions(),
networkId,
nodeKeys);
} else {

@ -1,11 +1,11 @@
{
"config": {
"chainId": 2017,
"homesteadBlock": 1,
"eip150Block": 2,
"homesteadBlock": 0,
"eip150Block": 0,
"eip150Hash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"eip155Block": 3,
"eip158Block": 3,
"eip155Block": 0,
"eip158Block": 0,
"ibft": {
"epochLength": 30000,

@ -18,6 +18,7 @@ include 'consensus:clique'
include 'consensus:common'
include 'consensus:ibft'
include 'consensus:ibftlegacy'
include 'config'
include 'crypto'
include 'ethereum:p2p'
include 'ethereum:mock-p2p'

Loading…
Cancel
Save