Common protocol schedule config handling (#250)

* Introduce ProtocolScheduleBuilder and use it for Clique, MainNet, IBFT and dev.

* Remove default milestone blocks and simplify MainnetProtocolSchedule. All milestone blocks must now be defined in the genesis file (previously ethash chains would get Mainnet milestone blocks by default).
Signed-off-by: Adrian Sutton <adrian.sutton@consensys.net>
pull/2/head
Adrian Sutton 6 years ago committed by GitHub
parent e899ba98b1
commit fd81e97ad9
  1. 3
      config/build.gradle
  2. 2
      config/src/main/java/tech/pegasys/pantheon/config/GenesisConfigFile.java
  3. 73
      config/src/main/java/tech/pegasys/pantheon/config/GenesisConfigOptions.java
  4. 102
      config/src/main/java/tech/pegasys/pantheon/config/JsonGenesisConfigOptions.java
  5. 122
      config/src/test-support/java/tech/pegasys/pantheon/config/StubGenesisConfigOptions.java
  6. 69
      consensus/clique/src/main/java/tech/pegasys/pantheon/consensus/clique/CliqueProtocolSchedule.java
  7. 87
      consensus/clique/src/main/java/tech/pegasys/pantheon/consensus/clique/CliqueProtocolSpecs.java
  8. 16
      consensus/clique/src/test/java/tech/pegasys/pantheon/consensus/clique/CliqueProtocolScheduleTest.java
  9. 57
      consensus/clique/src/test/java/tech/pegasys/pantheon/consensus/clique/CliqueProtocolSpecsTest.java
  10. 16
      consensus/clique/src/test/java/tech/pegasys/pantheon/consensus/clique/blockcreation/CliqueBlockCreatorTest.java
  11. 45
      consensus/ibftlegacy/src/main/java/tech/pegasys/pantheon/consensus/ibftlegacy/IbftProtocolSchedule.java
  12. 63
      consensus/ibftlegacy/src/main/java/tech/pegasys/pantheon/consensus/ibftlegacy/IbftProtocolSpecs.java
  13. 1
      ethereum/blockcreation/build.gradle
  14. 9
      ethereum/blockcreation/src/test/java/tech/pegasys/pantheon/ethereum/blockcreation/EthHashBlockCreatorTest.java
  15. 4
      ethereum/core/build.gradle
  16. 16
      ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/development/DevelopmentProtocolSchedule.java
  17. 36
      ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/development/DevelopmentProtocolSpecs.java
  18. 89
      ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/MainnetProtocolSchedule.java
  19. 90
      ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/MainnetProtocolSpecs.java
  20. 96
      ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/ProtocolScheduleBuilder.java
  21. 10
      ethereum/core/src/test-support/java/tech/pegasys/pantheon/ethereum/core/ExecutionContextTestFixture.java
  22. 43
      ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/mainnet/MainnetProtocolScheduleTest.java
  23. 78
      ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/vm/ReferenceTestProtocolSchedules.java
  24. 2
      ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/vm/VMReferenceTest.java
  25. 4
      ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/vm/operations/ConstantinopleSStoreOperationGasCostTest.java
  26. 3
      ethereum/jsonrpc/build.gradle
  27. 4
      ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcHttpServiceTest.java
  28. 5
      pantheon/src/main/java/tech/pegasys/pantheon/controller/MainnetPantheonController.java

@ -35,3 +35,6 @@ dependencies {
testImplementation 'org.mockito:mockito-core'
testImplementation 'junit:junit'
}
configurations { testArtifacts }
artifacts { testSupportArtifacts testSupportJar }

@ -59,7 +59,7 @@ public class GenesisConfigFile {
}
public GenesisConfigOptions getConfigOptions() {
return new GenesisConfigOptions(configRoot.getJsonObject("config"));
return new JsonGenesisConfigOptions(configRoot.getJsonObject("config"));
}
public Stream<GenesisAllocation> getAllocations() {

@ -15,76 +15,29 @@ package tech.pegasys.pantheon.config;
import java.util.OptionalInt;
import java.util.OptionalLong;
import io.vertx.core.json.JsonObject;
public interface GenesisConfigOptions {
public class GenesisConfigOptions {
boolean isEthHash();
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;
boolean isIbft();
GenesisConfigOptions(final JsonObject configRoot) {
this.configRoot = configRoot != null ? configRoot : new JsonObject();
}
boolean isClique();
public boolean isEthHash() {
return configRoot.containsKey(ETHASH_CONFIG_KEY);
}
IbftConfigOptions getIbftConfigOptions();
public boolean isIbft() {
return configRoot.containsKey(IBFT_CONFIG_KEY);
}
CliqueConfigOptions getCliqueConfigOptions();
public boolean isClique() {
return configRoot.containsKey(CLIQUE_CONFIG_KEY);
}
OptionalLong getHomesteadBlockNumber();
public IbftConfigOptions getIbftConfigOptions() {
return isIbft()
? new IbftConfigOptions(configRoot.getJsonObject(IBFT_CONFIG_KEY))
: IbftConfigOptions.DEFAULT;
}
OptionalLong getDaoForkBlock();
public CliqueConfigOptions getCliqueConfigOptions() {
return isClique()
? new CliqueConfigOptions(configRoot.getJsonObject(CLIQUE_CONFIG_KEY))
: CliqueConfigOptions.DEFAULT;
}
OptionalLong getTangerineWhistleBlockNumber();
public OptionalLong getHomesteadBlockNumber() {
return getOptionalLong("homesteadblock");
}
OptionalLong getSpuriousDragonBlockNumber();
public OptionalLong getDaoForkBlock() {
return getOptionalLong("daoforkblock");
}
OptionalLong getByzantiumBlockNumber();
public OptionalLong getTangerineWhistleBlockNumber() {
return getOptionalLong("eip150block");
}
OptionalLong getConstantinopleBlockNumber();
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();
}
OptionalInt getChainId();
}

@ -0,0 +1,102 @@
/*
* 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 JsonGenesisConfigOptions implements 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;
JsonGenesisConfigOptions(final JsonObject configRoot) {
this.configRoot = configRoot != null ? configRoot : new JsonObject();
}
@Override
public boolean isEthHash() {
return configRoot.containsKey(ETHASH_CONFIG_KEY);
}
@Override
public boolean isIbft() {
return configRoot.containsKey(IBFT_CONFIG_KEY);
}
@Override
public boolean isClique() {
return configRoot.containsKey(CLIQUE_CONFIG_KEY);
}
@Override
public IbftConfigOptions getIbftConfigOptions() {
return isIbft()
? new IbftConfigOptions(configRoot.getJsonObject(IBFT_CONFIG_KEY))
: IbftConfigOptions.DEFAULT;
}
@Override
public CliqueConfigOptions getCliqueConfigOptions() {
return isClique()
? new CliqueConfigOptions(configRoot.getJsonObject(CLIQUE_CONFIG_KEY))
: CliqueConfigOptions.DEFAULT;
}
@Override
public OptionalLong getHomesteadBlockNumber() {
return getOptionalLong("homesteadblock");
}
@Override
public OptionalLong getDaoForkBlock() {
return getOptionalLong("daoforkblock");
}
@Override
public OptionalLong getTangerineWhistleBlockNumber() {
return getOptionalLong("eip150block");
}
@Override
public OptionalLong getSpuriousDragonBlockNumber() {
return getOptionalLong("eip158block");
}
@Override
public OptionalLong getByzantiumBlockNumber() {
return getOptionalLong("byzantiumblock");
}
@Override
public OptionalLong getConstantinopleBlockNumber() {
return getOptionalLong("constantinopleblock");
}
@Override
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,122 @@
/*
* 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;
public class StubGenesisConfigOptions implements GenesisConfigOptions {
private OptionalLong homesteadBlockNumber = OptionalLong.empty();
private OptionalLong daoForkBlock = OptionalLong.empty();
private OptionalLong tangerineWhistleBlockNumber = OptionalLong.empty();
private OptionalLong spuriousDragonBlockNumber = OptionalLong.empty();
private OptionalLong byzantiumBlockNumber = OptionalLong.empty();
private OptionalLong constantinopleBlockNumber = OptionalLong.empty();
private OptionalInt chainId = OptionalInt.empty();
@Override
public boolean isEthHash() {
return true;
}
@Override
public boolean isIbft() {
return false;
}
@Override
public boolean isClique() {
return false;
}
@Override
public IbftConfigOptions getIbftConfigOptions() {
return IbftConfigOptions.DEFAULT;
}
@Override
public CliqueConfigOptions getCliqueConfigOptions() {
return CliqueConfigOptions.DEFAULT;
}
@Override
public OptionalLong getHomesteadBlockNumber() {
return homesteadBlockNumber;
}
@Override
public OptionalLong getDaoForkBlock() {
return daoForkBlock;
}
@Override
public OptionalLong getTangerineWhistleBlockNumber() {
return tangerineWhistleBlockNumber;
}
@Override
public OptionalLong getSpuriousDragonBlockNumber() {
return spuriousDragonBlockNumber;
}
@Override
public OptionalLong getByzantiumBlockNumber() {
return byzantiumBlockNumber;
}
@Override
public OptionalLong getConstantinopleBlockNumber() {
return constantinopleBlockNumber;
}
@Override
public OptionalInt getChainId() {
return chainId;
}
public StubGenesisConfigOptions homesteadBlock(final long blockNumber) {
homesteadBlockNumber = OptionalLong.of(blockNumber);
return this;
}
public StubGenesisConfigOptions daoForkBlock(final long blockNumber) {
daoForkBlock = OptionalLong.of(blockNumber);
return this;
}
public StubGenesisConfigOptions eip150Block(final long blockNumber) {
tangerineWhistleBlockNumber = OptionalLong.of(blockNumber);
return this;
}
public StubGenesisConfigOptions eip158Block(final long blockNumber) {
spuriousDragonBlockNumber = OptionalLong.of(blockNumber);
return this;
}
public StubGenesisConfigOptions byzantiumBlock(final long blockNumber) {
byzantiumBlockNumber = OptionalLong.of(blockNumber);
return this;
}
public StubGenesisConfigOptions constantinopleBlock(final long blockNumber) {
constantinopleBlockNumber = OptionalLong.of(blockNumber);
return this;
}
public StubGenesisConfigOptions chainId(final int chainId) {
this.chainId = OptionalInt.of(chainId);
return this;
}
}

@ -12,12 +12,20 @@
*/
package tech.pegasys.pantheon.consensus.clique;
import static tech.pegasys.pantheon.consensus.clique.BlockHeaderValidationRulesetFactory.cliqueBlockHeaderValidator;
import tech.pegasys.pantheon.config.CliqueConfigOptions;
import tech.pegasys.pantheon.config.GenesisConfigOptions;
import tech.pegasys.pantheon.consensus.common.EpochManager;
import tech.pegasys.pantheon.crypto.SECP256K1.KeyPair;
import tech.pegasys.pantheon.ethereum.core.Address;
import tech.pegasys.pantheon.ethereum.core.Util;
import tech.pegasys.pantheon.ethereum.mainnet.MutableProtocolSchedule;
import tech.pegasys.pantheon.ethereum.core.Wei;
import tech.pegasys.pantheon.ethereum.mainnet.MainnetBlockBodyValidator;
import tech.pegasys.pantheon.ethereum.mainnet.MainnetBlockImporter;
import tech.pegasys.pantheon.ethereum.mainnet.ProtocolSchedule;
import tech.pegasys.pantheon.ethereum.mainnet.ProtocolScheduleBuilder;
import tech.pegasys.pantheon.ethereum.mainnet.ProtocolSpecBuilder;
/** Defines the protocol behaviours for a blockchain using Clique. */
public class CliqueProtocolSchedule {
@ -27,44 +35,33 @@ public class CliqueProtocolSchedule {
public static ProtocolSchedule<CliqueContext> create(
final GenesisConfigOptions config, final KeyPair nodeKeys) {
// Get Config Data
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 MutableProtocolSchedule<>(chainId);
// TODO(tmm) replace address with passed in node data (coming later)
final CliqueProtocolSpecs specs =
new CliqueProtocolSpecs(
blockPeriod,
epochLength,
Util.publicKeyToAddress(nodeKeys.getPublicKey()),
protocolSchedule);
final Address localNodeAddress = Util.publicKeyToAddress(nodeKeys.getPublicKey());
protocolSchedule.putMilestone(0, specs.frontier());
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()));
config
.getConstantinopleBlockNumber()
.ifPresent(
blockNumber -> protocolSchedule.putMilestone(blockNumber, specs.constantinople()));
final EpochManager epochManager = new EpochManager(cliqueConfig.getEpochLength());
return new ProtocolScheduleBuilder<>(
config,
DEFAULT_CHAIN_ID,
builder ->
applyCliqueSpecificModifications(
epochManager, cliqueConfig.getBlockPeriodSeconds(), localNodeAddress, builder))
.createProtocolSchedule();
}
return protocolSchedule;
private static ProtocolSpecBuilder<CliqueContext> applyCliqueSpecificModifications(
final EpochManager epochManager,
final long secondsBetweenBlocks,
final Address localNodeAddress,
final ProtocolSpecBuilder<Void> specBuilder) {
return specBuilder
.changeConsensusContextType(
difficultyCalculator -> cliqueBlockHeaderValidator(secondsBetweenBlocks, epochManager),
difficultyCalculator -> cliqueBlockHeaderValidator(secondsBetweenBlocks, epochManager),
MainnetBlockBodyValidator::new,
MainnetBlockImporter::new,
new CliqueDifficultyCalculator(localNodeAddress))
.blockReward(Wei.ZERO)
.miningBeneficiaryCalculator(CliqueHelpers::getProposerOfBlock);
}
}

@ -1,87 +0,0 @@
/*
* 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.consensus.clique;
import static tech.pegasys.pantheon.consensus.clique.BlockHeaderValidationRulesetFactory.cliqueBlockHeaderValidator;
import tech.pegasys.pantheon.consensus.common.EpochManager;
import tech.pegasys.pantheon.ethereum.core.Address;
import tech.pegasys.pantheon.ethereum.core.Wei;
import tech.pegasys.pantheon.ethereum.mainnet.MainnetBlockBodyValidator;
import tech.pegasys.pantheon.ethereum.mainnet.MainnetBlockImporter;
import tech.pegasys.pantheon.ethereum.mainnet.MainnetProtocolSpecs;
import tech.pegasys.pantheon.ethereum.mainnet.ProtocolSchedule;
import tech.pegasys.pantheon.ethereum.mainnet.ProtocolSpec;
import tech.pegasys.pantheon.ethereum.mainnet.ProtocolSpecBuilder;
/** Factory for producing Clique protocol specs for given configurations and known fork points */
public class CliqueProtocolSpecs {
private final long secondsBetweenBlocks;
private final long epochLength;
private final Address localNodeAddress;
private final ProtocolSchedule<CliqueContext> protocolSchedule;
public CliqueProtocolSpecs(
final long secondsBetweenBlocks,
final long epochLength,
final Address localNodeAddress,
final ProtocolSchedule<CliqueContext> protocolSchedule) {
this.secondsBetweenBlocks = secondsBetweenBlocks;
this.epochLength = epochLength;
this.localNodeAddress = localNodeAddress;
this.protocolSchedule = protocolSchedule;
}
public ProtocolSpec<CliqueContext> frontier() {
return applyCliqueSpecificModifications(MainnetProtocolSpecs.frontierDefinition());
}
public ProtocolSpec<CliqueContext> homestead() {
return applyCliqueSpecificModifications(MainnetProtocolSpecs.homesteadDefinition());
}
public ProtocolSpec<CliqueContext> tangerineWhistle() {
return applyCliqueSpecificModifications(MainnetProtocolSpecs.tangerineWhistleDefinition());
}
public ProtocolSpec<CliqueContext> spuriousDragon() {
return applyCliqueSpecificModifications(
MainnetProtocolSpecs.spuriousDragonDefinition(protocolSchedule.getChainId()));
}
public ProtocolSpec<CliqueContext> byzantium() {
return applyCliqueSpecificModifications(
MainnetProtocolSpecs.byzantiumDefinition(protocolSchedule.getChainId()));
}
public ProtocolSpec<CliqueContext> constantinople() {
return applyCliqueSpecificModifications(
MainnetProtocolSpecs.constantinopleDefinition(protocolSchedule.getChainId()));
}
private ProtocolSpec<CliqueContext> applyCliqueSpecificModifications(
final ProtocolSpecBuilder<Void> specBuilder) {
final EpochManager epochManager = new EpochManager(epochLength);
return specBuilder
.<CliqueContext>changeConsensusContextType(
difficultyCalculator -> cliqueBlockHeaderValidator(secondsBetweenBlocks, epochManager),
difficultyCalculator -> cliqueBlockHeaderValidator(secondsBetweenBlocks, epochManager),
MainnetBlockBodyValidator::new,
MainnetBlockImporter::new,
new CliqueDifficultyCalculator(localNodeAddress))
.blockReward(Wei.ZERO)
.miningBeneficiaryCalculator(CliqueHelpers::getProposerOfBlock)
.build(protocolSchedule);
}
}

@ -17,6 +17,7 @@ import static org.assertj.core.api.Java6Assertions.assertThat;
import tech.pegasys.pantheon.config.GenesisConfigFile;
import tech.pegasys.pantheon.config.GenesisConfigOptions;
import tech.pegasys.pantheon.crypto.SECP256K1.KeyPair;
import tech.pegasys.pantheon.ethereum.core.Wei;
import tech.pegasys.pantheon.ethereum.mainnet.ProtocolSchedule;
import tech.pegasys.pantheon.ethereum.mainnet.ProtocolSpec;
@ -24,6 +25,8 @@ import org.junit.Test;
public class CliqueProtocolScheduleTest {
private static final KeyPair NODE_KEYS = KeyPair.generate();
@Test
public void protocolSpecsAreCreatedAtBlockDefinedInJson() {
final String jsonInput =
@ -38,7 +41,7 @@ public class CliqueProtocolScheduleTest {
final GenesisConfigOptions config = GenesisConfigFile.fromConfig(jsonInput).getConfigOptions();
final ProtocolSchedule<CliqueContext> protocolSchedule =
CliqueProtocolSchedule.create(config, KeyPair.generate());
CliqueProtocolSchedule.create(config, NODE_KEYS);
final ProtocolSpec<CliqueContext> homesteadSpec = protocolSchedule.getByBlockNumber(1);
final ProtocolSpec<CliqueContext> tangerineWhistleSpec = protocolSchedule.getByBlockNumber(2);
@ -49,4 +52,15 @@ public class CliqueProtocolScheduleTest {
assertThat(tangerineWhistleSpec.equals(spuriousDragonSpec)).isFalse();
assertThat(spuriousDragonSpec.equals(byzantiumSpec)).isFalse();
}
@Test
public void parametersAlignWithMainnetWithAdjustments() {
final ProtocolSpec<CliqueContext> homestead =
CliqueProtocolSchedule.create(GenesisConfigFile.DEFAULT.getConfigOptions(), NODE_KEYS)
.getByBlockNumber(0);
assertThat(homestead.getName()).isEqualTo("Frontier");
assertThat(homestead.getBlockReward()).isEqualTo(Wei.ZERO);
assertThat(homestead.getDifficultyCalculator()).isInstanceOf(CliqueDifficultyCalculator.class);
}
}

@ -1,57 +0,0 @@
/*
* 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.consensus.clique;
import static org.assertj.core.api.Java6Assertions.assertThat;
import tech.pegasys.pantheon.ethereum.core.AddressHelpers;
import tech.pegasys.pantheon.ethereum.core.Wei;
import tech.pegasys.pantheon.ethereum.mainnet.MainnetProtocolSpecs;
import tech.pegasys.pantheon.ethereum.mainnet.MutableProtocolSchedule;
import tech.pegasys.pantheon.ethereum.mainnet.ProtocolSchedule;
import tech.pegasys.pantheon.ethereum.mainnet.ProtocolSpec;
import org.junit.Test;
public class CliqueProtocolSpecsTest {
private static final int CHAIN_ID = 5;
CliqueProtocolSpecs protocolSpecs =
new CliqueProtocolSpecs(
15, 30_000, AddressHelpers.ofValue(5), new MutableProtocolSchedule<>(CHAIN_ID));
@Test
public void homsteadParametersAlignWithMainnetWithAdjustments() {
final ProtocolSpec<CliqueContext> homestead = protocolSpecs.homestead();
assertThat(homestead.getName()).isEqualTo("Homestead");
assertThat(homestead.getBlockReward()).isEqualTo(Wei.ZERO);
assertThat(homestead.getDifficultyCalculator()).isInstanceOf(CliqueDifficultyCalculator.class);
}
@Test
public void allSpecsInheritFromMainnetCounterparts() {
final ProtocolSchedule<Void> mainnetProtocolSchedule = new MutableProtocolSchedule<>(CHAIN_ID);
assertThat(protocolSpecs.frontier().getName())
.isEqualTo(MainnetProtocolSpecs.frontier(mainnetProtocolSchedule).getName());
assertThat(protocolSpecs.homestead().getName())
.isEqualTo(MainnetProtocolSpecs.homestead(mainnetProtocolSchedule).getName());
assertThat(protocolSpecs.tangerineWhistle().getName())
.isEqualTo(MainnetProtocolSpecs.tangerineWhistle(mainnetProtocolSchedule).getName());
assertThat(protocolSpecs.spuriousDragon().getName())
.isEqualTo(MainnetProtocolSpecs.spuriousDragon(1, mainnetProtocolSchedule).getName());
assertThat(protocolSpecs.byzantium().getName())
.isEqualTo(MainnetProtocolSpecs.byzantium(1, mainnetProtocolSchedule).getName());
}
}

@ -23,7 +23,7 @@ import tech.pegasys.pantheon.config.GenesisConfigFile;
import tech.pegasys.pantheon.consensus.clique.CliqueContext;
import tech.pegasys.pantheon.consensus.clique.CliqueExtraData;
import tech.pegasys.pantheon.consensus.clique.CliqueHelpers;
import tech.pegasys.pantheon.consensus.clique.CliqueProtocolSpecs;
import tech.pegasys.pantheon.consensus.clique.CliqueProtocolSchedule;
import tech.pegasys.pantheon.consensus.clique.TestHelpers;
import tech.pegasys.pantheon.consensus.clique.VoteTallyCache;
import tech.pegasys.pantheon.consensus.common.VoteProposer;
@ -42,7 +42,7 @@ import tech.pegasys.pantheon.ethereum.core.PendingTransactions;
import tech.pegasys.pantheon.ethereum.core.Util;
import tech.pegasys.pantheon.ethereum.core.Wei;
import tech.pegasys.pantheon.ethereum.db.WorldStateArchive;
import tech.pegasys.pantheon.ethereum.mainnet.MutableProtocolSchedule;
import tech.pegasys.pantheon.ethereum.mainnet.ProtocolSchedule;
import tech.pegasys.pantheon.util.bytes.BytesValue;
import java.util.List;
@ -53,14 +53,12 @@ import org.junit.Test;
public class CliqueBlockCreatorTest {
private static final int CHAIN_ID = 1;
private final KeyPair proposerKeyPair = KeyPair.generate();
private final Address proposerAddress = Util.publicKeyToAddress(proposerKeyPair.getPublicKey());
private final KeyPair otherKeyPair = KeyPair.generate();
private final List<Address> validatorList = Lists.newArrayList();
private final MutableProtocolSchedule<CliqueContext> protocolSchedule =
new MutableProtocolSchedule<>(CHAIN_ID);
private ProtocolSchedule<CliqueContext> protocolSchedule;
private final WorldStateArchive stateArchive = createInMemoryWorldStateArchive();
private MutableBlockchain blockchain;
@ -69,11 +67,9 @@ public class CliqueBlockCreatorTest {
@Before
public void setup() {
final CliqueProtocolSpecs specs =
new CliqueProtocolSpecs(
15, 30_000, Util.publicKeyToAddress(proposerKeyPair.getPublicKey()), protocolSchedule);
protocolSchedule.putMilestone(0, specs.frontier());
protocolSchedule =
CliqueProtocolSchedule.create(
GenesisConfigFile.DEFAULT.getConfigOptions(), proposerKeyPair);
final Address otherAddress = Util.publicKeyToAddress(otherKeyPair.getPublicKey());
validatorList.add(otherAddress);

@ -12,11 +12,21 @@
*/
package tech.pegasys.pantheon.consensus.ibftlegacy;
import static tech.pegasys.pantheon.consensus.ibftlegacy.IbftBlockHeaderValidationRulesetFactory.ibftBlockHeaderValidator;
import tech.pegasys.pantheon.config.GenesisConfigOptions;
import tech.pegasys.pantheon.config.IbftConfigOptions;
import tech.pegasys.pantheon.consensus.common.EpochManager;
import tech.pegasys.pantheon.consensus.ibft.IbftBlockImporter;
import tech.pegasys.pantheon.consensus.ibft.IbftContext;
import tech.pegasys.pantheon.ethereum.mainnet.MutableProtocolSchedule;
import tech.pegasys.pantheon.ethereum.core.Wei;
import tech.pegasys.pantheon.ethereum.mainnet.MainnetBlockBodyValidator;
import tech.pegasys.pantheon.ethereum.mainnet.MainnetBlockImporter;
import tech.pegasys.pantheon.ethereum.mainnet.ProtocolSchedule;
import tech.pegasys.pantheon.ethereum.mainnet.ProtocolScheduleBuilder;
import tech.pegasys.pantheon.ethereum.mainnet.ProtocolSpecBuilder;
import java.math.BigInteger;
/** Defines the protocol behaviours for a blockchain using IBFT. */
public class IbftProtocolSchedule {
@ -24,17 +34,34 @@ public class IbftProtocolSchedule {
private static final int DEFAULT_CHAIN_ID = 1;
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 EpochManager epochManager = new EpochManager(epochLength);
return new ProtocolScheduleBuilder<>(
config,
DEFAULT_CHAIN_ID,
builder -> applyIbftChanges(blockPeriod, epochManager, builder))
.createProtocolSchedule();
}
final MutableProtocolSchedule<IbftContext> protocolSchedule =
new MutableProtocolSchedule<>(chainId);
protocolSchedule.putMilestone(
spuriousDragonBlock,
IbftProtocolSpecs.spuriousDragon(blockPeriod, epochLength, chainId, protocolSchedule));
return protocolSchedule;
private static ProtocolSpecBuilder<IbftContext> applyIbftChanges(
final long secondsBetweenBlocks,
final EpochManager epochManager,
final ProtocolSpecBuilder<Void> builder) {
return builder
.<IbftContext>changeConsensusContextType(
difficultyCalculator -> ibftBlockHeaderValidator(secondsBetweenBlocks),
difficultyCalculator -> ibftBlockHeaderValidator(secondsBetweenBlocks),
MainnetBlockBodyValidator::new,
(blockHeaderValidator, blockBodyValidator, blockProcessor) ->
new IbftBlockImporter(
new MainnetBlockImporter<>(
blockHeaderValidator, blockBodyValidator, blockProcessor),
new IbftVoteTallyUpdater(epochManager)),
(time, parent, protocolContext) -> BigInteger.ONE)
.blockReward(Wei.ZERO)
.blockHashFunction(IbftBlockHashing::calculateHashOfIbftBlockOnChain);
}
}

@ -1,63 +0,0 @@
/*
* 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.consensus.ibftlegacy;
import static tech.pegasys.pantheon.consensus.ibftlegacy.IbftBlockHeaderValidationRulesetFactory.ibftBlockHeaderValidator;
import tech.pegasys.pantheon.consensus.common.EpochManager;
import tech.pegasys.pantheon.consensus.ibft.IbftBlockImporter;
import tech.pegasys.pantheon.consensus.ibft.IbftContext;
import tech.pegasys.pantheon.ethereum.core.Wei;
import tech.pegasys.pantheon.ethereum.mainnet.MainnetBlockBodyValidator;
import tech.pegasys.pantheon.ethereum.mainnet.MainnetBlockImporter;
import tech.pegasys.pantheon.ethereum.mainnet.MainnetProtocolSpecs;
import tech.pegasys.pantheon.ethereum.mainnet.ProtocolSchedule;
import tech.pegasys.pantheon.ethereum.mainnet.ProtocolSpec;
import java.math.BigInteger;
/** Factory for producing Ibft protocol specs for given configurations and known fork points */
public class IbftProtocolSpecs {
/**
* Produce the ProtocolSpec for an IBFT chain that uses spurious dragon milestone configuration
*
* @param secondsBetweenBlocks the block period in seconds
* @param epochLength the number of blocks in each epoch
* @param chainId the id of the Chain.
* @param protocolSchedule the {@link ProtocolSchedule} this spec will be part of
* @return a configured ProtocolSpec for dealing with IBFT blocks
*/
public static ProtocolSpec<IbftContext> spuriousDragon(
final long secondsBetweenBlocks,
final long epochLength,
final int chainId,
final ProtocolSchedule<IbftContext> protocolSchedule) {
final EpochManager epochManager = new EpochManager(epochLength);
return MainnetProtocolSpecs.spuriousDragonDefinition(chainId)
.<IbftContext>changeConsensusContextType(
difficultyCalculator -> ibftBlockHeaderValidator(secondsBetweenBlocks),
difficultyCalculator -> ibftBlockHeaderValidator(secondsBetweenBlocks),
MainnetBlockBodyValidator::new,
(blockHeaderValidator, blockBodyValidator, blockProcessor) ->
new IbftBlockImporter(
new MainnetBlockImporter<>(
blockHeaderValidator, blockBodyValidator, blockProcessor),
new IbftVoteTallyUpdater(epochManager)),
(time, parent, protocolContext) -> BigInteger.ONE)
.blockReward(Wei.ZERO)
.blockHashFunction(IbftBlockHashing::calculateHashOfIbftBlockOnChain)
.name("IBFT")
.build(protocolSchedule);
}
}

@ -23,6 +23,7 @@ dependencies {
testImplementation 'org.assertj:assertj-core'
testImplementation 'org.awaitility:awaitility'
testImplementation 'org.mockito:mockito-core'
testImplementation project(path: ':config', configuration: 'testSupportArtifacts')
testImplementation project(path: ':ethereum:core', configuration: 'testSupportArtifacts')
testImplementation project(path: ':ethereum:core', configuration: 'testArtifacts')
}

@ -12,6 +12,7 @@
*/
package tech.pegasys.pantheon.ethereum.blockcreation;
import tech.pegasys.pantheon.config.GenesisConfigFile;
import tech.pegasys.pantheon.ethereum.core.Address;
import tech.pegasys.pantheon.ethereum.core.Block;
import tech.pegasys.pantheon.ethereum.core.ExecutionContextTestFixture;
@ -19,11 +20,12 @@ import tech.pegasys.pantheon.ethereum.core.PendingTransactions;
import tech.pegasys.pantheon.ethereum.core.Wei;
import tech.pegasys.pantheon.ethereum.mainnet.EthHashSolver;
import tech.pegasys.pantheon.ethereum.mainnet.EthHasher.Light;
import tech.pegasys.pantheon.ethereum.mainnet.MainnetProtocolSchedule;
import tech.pegasys.pantheon.ethereum.mainnet.ProtocolScheduleBuilder;
import tech.pegasys.pantheon.ethereum.mainnet.ValidationTestUtils;
import tech.pegasys.pantheon.util.bytes.BytesValue;
import java.io.IOException;
import java.util.function.Function;
import com.google.common.collect.Lists;
import org.assertj.core.api.Assertions;
@ -43,7 +45,10 @@ public class EthHashBlockCreatorTest {
private final ExecutionContextTestFixture executionContextTestFixture =
ExecutionContextTestFixture.builder()
.protocolSchedule(MainnetProtocolSchedule.create(2, 3, 10, 11, 12, -1, 42))
.protocolSchedule(
new ProtocolScheduleBuilder<>(
GenesisConfigFile.DEFAULT.getConfigOptions(), 42, Function.identity())
.createProtocolSchedule())
.build();
@Test

@ -36,17 +36,20 @@ dependencies {
runtime 'org.apache.logging.log4j:log4j-core'
testImplementation project(path:':ethereum:referencetests', configuration: 'testOutput')
testImplementation project( path: ':config', configuration: 'testSupportArtifacts')
testImplementation project(':testutil')
testImplementation 'org.assertj:assertj-core'
testImplementation 'org.mockito:mockito-core'
testImplementation 'junit:junit'
integrationTestImplementation project(path: ':config', configuration: 'testSupportArtifacts')
integrationTestImplementation 'org.assertj:assertj-core'
integrationTestImplementation 'org.mockito:mockito-core'
integrationTestImplementation 'junit:junit'
testSupportImplementation project(':testutil')
testSupportImplementation project( path: ':config', configuration: 'testSupportArtifacts')
testSupportImplementation 'org.assertj:assertj-core'
testSupportImplementation 'org.mockito:mockito-core'
@ -54,6 +57,7 @@ dependencies {
jmhImplementation project(':util')
jmhImplementation project( path: ':ethereum:core', configuration: 'testSupportArtifacts')
jmhImplementation project(path: ':config', configuration: 'testSupportArtifacts')
jmhImplementation project(':crypto')
jmhImplementation project(':ethereum:rlp')
jmhImplementation project(':ethereum:trie')

@ -15,19 +15,17 @@ package tech.pegasys.pantheon.ethereum.development;
import static tech.pegasys.pantheon.ethereum.mainnet.MainnetTransactionValidator.NO_CHAIN_ID;
import tech.pegasys.pantheon.config.GenesisConfigOptions;
import tech.pegasys.pantheon.ethereum.mainnet.MutableProtocolSchedule;
import tech.pegasys.pantheon.ethereum.mainnet.ProtocolSchedule;
import tech.pegasys.pantheon.ethereum.mainnet.ProtocolScheduleBuilder;
/**
* A mock ProtocolSchedule which behaves similarly to Byzantium (but for all blocks), albeit with a
* much reduced difficulty (which supports testing on CPU alone).
*/
/** A ProtocolSchedule which behaves similarly to MainNet, but with a much reduced difficulty. */
public class DevelopmentProtocolSchedule {
public static ProtocolSchedule<Void> create(final GenesisConfigOptions config) {
final Integer chainId = config.getChainId().orElse(NO_CHAIN_ID);
final MutableProtocolSchedule<Void> protocolSchedule = new MutableProtocolSchedule<>(chainId);
protocolSchedule.putMilestone(0, DevelopmentProtocolSpecs.first(chainId, protocolSchedule));
return protocolSchedule;
return new ProtocolScheduleBuilder<>(
config,
NO_CHAIN_ID,
builder -> builder.difficultyCalculator(DevelopmentDifficultyCalculators.DEVELOPER))
.createProtocolSchedule();
}
}

@ -1,36 +0,0 @@
/*
* 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.ethereum.development;
import tech.pegasys.pantheon.ethereum.mainnet.MainnetProtocolSpecs;
import tech.pegasys.pantheon.ethereum.mainnet.ProtocolSchedule;
import tech.pegasys.pantheon.ethereum.mainnet.ProtocolSpec;
/**
* Provides a protocol specification which is suitable for use on private, PoW networks, where block
* mining is performed on CPUs alone.
*/
public class DevelopmentProtocolSpecs {
/*
* The DevelopmentProtocolSpecification is the same as the byzantium spec, but with a much reduced
* difficulty calculator (to support CPU mining).
*/
public static ProtocolSpec<Void> first(
final Integer chainId, final ProtocolSchedule<Void> protocolSchedule) {
return MainnetProtocolSpecs.byzantiumDefinition(chainId)
.difficultyCalculator(DevelopmentDifficultyCalculators.DEVELOPER)
.name("first")
.build(protocolSchedule);
}
}

@ -12,77 +12,18 @@
*/
package tech.pegasys.pantheon.ethereum.mainnet;
import tech.pegasys.pantheon.config.GenesisConfigFile;
import tech.pegasys.pantheon.config.GenesisConfigOptions;
import java.util.function.Function;
/** Provides {@link ProtocolSpec} lookups for mainnet hard forks. */
public class MainnetProtocolSchedule {
private static final long DEFAULT_HOMESTEAD_BLOCK_NUMBER = 1_150_000L;
private static final long DEFAULT_DAO_BLOCK_NUMBER = 1_920_000L;
private static final long DEFAULT_TANGERINE_WHISTLE_BLOCK_NUMBER = 2_463_000L;
private static final long DEFAULT_SPURIOUS_DRAGON_BLOCK_NUMBER = 2_675_000L;
private static final long DEFAULT_BYZANTIUM_BLOCK_NUMBER = 4_730_000L;
// Start of Constantinople has not yet been set.
private static final long DEFAULT_CONSTANTINOPLE_BLOCK_NUMBER = -1L;
public static final int DEFAULT_CHAIN_ID = 1;
/**
* Creates a mainnet protocol schedule with milestones starting at the specified block numbers
*
* @param homesteadBlockNumber Block number at which to start the homestead fork
* @param daoBlockNumber Block number at which to start the dao fork
* @param tangerineWhistleBlockNumber Block number at which to start the tangerine whistle fork
* @param spuriousDragonBlockNumber Block number at which to start the spurious dragon fork
* @param byzantiumBlockNumber Block number at which to start the byzantium fork
* @param constantinopleBlockNumber Block number at which to start the constantinople fork
* @param chainId ID of the blockchain
* @return MainnetProtocolSchedule return newly instantiated protocol schedule
*/
public static ProtocolSchedule<Void> create(
final long homesteadBlockNumber,
final long daoBlockNumber,
final long tangerineWhistleBlockNumber,
final long spuriousDragonBlockNumber,
final long byzantiumBlockNumber,
final long constantinopleBlockNumber,
final int chainId) {
final MutableProtocolSchedule<Void> protocolSchedule = new MutableProtocolSchedule<>(chainId);
protocolSchedule.putMilestone(0, MainnetProtocolSpecs.frontier(protocolSchedule));
final ProtocolSpec<Void> homestead = MainnetProtocolSpecs.homestead(protocolSchedule);
protocolSchedule.putMilestone(homesteadBlockNumber, homestead);
if (daoBlockNumber != 0) {
protocolSchedule.putMilestone(
daoBlockNumber, MainnetProtocolSpecs.daoRecoveryInit(protocolSchedule));
protocolSchedule.putMilestone(
daoBlockNumber + 1, MainnetProtocolSpecs.daoRecoveryTransition(protocolSchedule));
protocolSchedule.putMilestone(daoBlockNumber + 10, homestead);
}
protocolSchedule.putMilestone(
tangerineWhistleBlockNumber, MainnetProtocolSpecs.tangerineWhistle(protocolSchedule));
protocolSchedule.putMilestone(
spuriousDragonBlockNumber, MainnetProtocolSpecs.spuriousDragon(chainId, protocolSchedule));
protocolSchedule.putMilestone(
byzantiumBlockNumber, MainnetProtocolSpecs.byzantium(chainId, protocolSchedule));
if (constantinopleBlockNumber >= 0) {
protocolSchedule.putMilestone(
constantinopleBlockNumber,
MainnetProtocolSpecs.constantinople(chainId, protocolSchedule));
}
return protocolSchedule;
}
public static ProtocolSchedule<Void> create() {
return create(
DEFAULT_HOMESTEAD_BLOCK_NUMBER,
DEFAULT_DAO_BLOCK_NUMBER,
DEFAULT_TANGERINE_WHISTLE_BLOCK_NUMBER,
DEFAULT_SPURIOUS_DRAGON_BLOCK_NUMBER,
DEFAULT_BYZANTIUM_BLOCK_NUMBER,
DEFAULT_CONSTANTINOPLE_BLOCK_NUMBER,
DEFAULT_CHAIN_ID);
return fromConfig(GenesisConfigFile.mainnet().getConfigOptions());
}
/**
@ -93,25 +34,7 @@ public class MainnetProtocolSchedule {
* @return A configured mainnet protocol schedule
*/
public static ProtocolSchedule<Void> fromConfig(final GenesisConfigOptions config) {
final long homesteadBlockNumber =
config.getHomesteadBlockNumber().orElse(DEFAULT_HOMESTEAD_BLOCK_NUMBER);
final long daoBlockNumber = config.getDaoForkBlock().orElse(DEFAULT_DAO_BLOCK_NUMBER);
final long tangerineWhistleBlockNumber =
config.getTangerineWhistleBlockNumber().orElse(DEFAULT_TANGERINE_WHISTLE_BLOCK_NUMBER);
final long spuriousDragonBlockNumber =
config.getSpuriousDragonBlockNumber().orElse(DEFAULT_SPURIOUS_DRAGON_BLOCK_NUMBER);
final long byzantiumBlockNumber =
config.getByzantiumBlockNumber().orElse(DEFAULT_BYZANTIUM_BLOCK_NUMBER);
final long constantinopleBlockNumber =
config.getConstantinopleBlockNumber().orElse(DEFAULT_CONSTANTINOPLE_BLOCK_NUMBER);
final int chainId = config.getChainId().orElse(DEFAULT_CHAIN_ID);
return create(
homesteadBlockNumber,
daoBlockNumber,
tangerineWhistleBlockNumber,
spuriousDragonBlockNumber,
byzantiumBlockNumber,
constantinopleBlockNumber,
chainId);
return new ProtocolScheduleBuilder<>(config, DEFAULT_CHAIN_ID, Function.identity())
.createProtocolSchedule();
}
}

@ -94,26 +94,6 @@ public abstract class MainnetProtocolSpecs {
.name("Frontier");
}
/**
* Returns the Frontier milestone protocol spec.
*
* @param protocolSchedule the {@link ProtocolSchedule} this spec will be part of
* @return the Frontier milestone protocol spec
*/
public static ProtocolSpec<Void> frontier(final ProtocolSchedule<Void> protocolSchedule) {
return frontierDefinition().build(protocolSchedule);
}
/**
* Returns the Homestead milestone protocol spec.
*
* @param protocolSchedule the {@link ProtocolSchedule} this spec will be part of
* @return the Homestead milestone protocol spec
*/
public static ProtocolSpec<Void> homestead(final ProtocolSchedule<Void> protocolSchedule) {
return homesteadDefinition().build(protocolSchedule);
}
public static ProtocolSpecBuilder<Void> homesteadDefinition() {
return frontierDefinition()
.gasCalculator(HomesteadGasCalculator::new)
@ -128,17 +108,7 @@ public abstract class MainnetProtocolSpecs {
.name("Homestead");
}
/**
* Returns the initial DAO block milestone protocol spec.
*
* @param protocolSchedule the {@link ProtocolSchedule} this spec will be part of
* @return the initial DAO block milestone protocol spec
*/
public static ProtocolSpec<Void> daoRecoveryInit(final ProtocolSchedule<Void> protocolSchedule) {
return daoRecoveryInitDefinition().build(protocolSchedule);
}
private static ProtocolSpecBuilder<Void> daoRecoveryInitDefinition() {
public static ProtocolSpecBuilder<Void> daoRecoveryInitDefinition() {
return homesteadDefinition()
.blockHeaderValidatorBuilder(MainnetBlockHeaderValidator::createDaoValidator)
.blockProcessorBuilder(
@ -155,28 +125,10 @@ public abstract class MainnetProtocolSpecs {
.name("DaoRecoveryInit");
}
/**
* Returns the DAO block transition segment milestone protocol spec.
*
* @param protocolSchedule the {@link ProtocolSchedule} this spec will be part of
* @return the DAO block transition segment milestone protocol spec
*/
public static ProtocolSpec<Void> daoRecoveryTransition(
final ProtocolSchedule<Void> protocolSchedule) {
public static ProtocolSpecBuilder<Void> daoRecoveryTransitionDefinition() {
return daoRecoveryInitDefinition()
.blockProcessorBuilder(MainnetBlockProcessor::new)
.name("DaoRecoveryTransition")
.build(protocolSchedule);
}
/**
* Returns the Tangerine Whistle milestone protocol spec.
*
* @param protocolSchedule the {@link ProtocolSchedule} this spec will be part of
* @return the Tangerine Whistle milestone protocol spec
*/
public static ProtocolSpec<Void> tangerineWhistle(final ProtocolSchedule<Void> protocolSchedule) {
return tangerineWhistleDefinition().build(protocolSchedule);
.name("DaoRecoveryTransition");
}
public static ProtocolSpecBuilder<Void> tangerineWhistleDefinition() {
@ -185,18 +137,6 @@ public abstract class MainnetProtocolSpecs {
.name("TangerineWhistle");
}
/**
* Returns the Spurious Dragon milestone protocol spec.
*
* @param chainId ID of the blockchain
* @param protocolSchedule the {@link ProtocolSchedule} this spec will be part of
* @return the Spurious Dragon milestone protocol spec
*/
public static ProtocolSpec<Void> spuriousDragon(
final int chainId, final ProtocolSchedule<Void> protocolSchedule) {
return spuriousDragonDefinition(chainId).build(protocolSchedule);
}
public static ProtocolSpecBuilder<Void> spuriousDragonDefinition(final int chainId) {
return tangerineWhistleDefinition()
.gasCalculator(SpuriousDragonGasCalculator::new)
@ -231,18 +171,6 @@ public abstract class MainnetProtocolSpecs {
.name("SpuriousDragon");
}
/**
* Returns the Byzantium milestone protocol spec.
*
* @param chainId ID of the blockchain
* @param protocolSchedule the {@link ProtocolSchedule} this spec will be part of
* @return the Byzantium milestone protocol spec
*/
public static ProtocolSpec<Void> byzantium(
final int chainId, final ProtocolSchedule<Void> protocolSchedule) {
return byzantiumDefinition(chainId).build(protocolSchedule);
}
public static ProtocolSpecBuilder<Void> byzantiumDefinition(final int chainId) {
return spuriousDragonDefinition(chainId)
.evmBuilder(MainnetEvmRegistries::byzantium)
@ -254,18 +182,6 @@ public abstract class MainnetProtocolSpecs {
.name("Byzantium");
}
/**
* Returns the Constantinople milestone protocol spec.
*
* @param chainId ID of the blockchain
* @param protocolSchedule the {@link ProtocolSchedule} this spec will be part of
* @return the Constantinople milestone protocol spec
*/
public static ProtocolSpec<Void> constantinople(
final int chainId, final ProtocolSchedule<Void> protocolSchedule) {
return constantinopleDefinition(chainId).build(protocolSchedule);
}
public static ProtocolSpecBuilder<Void> constantinopleDefinition(final int chainId) {
return byzantiumDefinition(chainId)
.difficultyCalculator(MainnetDifficultyCalculators.CONSTANTINOPLE)

@ -0,0 +1,96 @@
/*
* 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.ethereum.mainnet;
import tech.pegasys.pantheon.config.GenesisConfigOptions;
import java.util.OptionalLong;
import java.util.function.Function;
public class ProtocolScheduleBuilder<C> {
private final GenesisConfigOptions config;
private final Function<ProtocolSpecBuilder<Void>, ProtocolSpecBuilder<C>> protocolSpecAdapter;
private final int defaultChainId;
public ProtocolScheduleBuilder(
final GenesisConfigOptions config,
final int defaultChainId,
final Function<ProtocolSpecBuilder<Void>, ProtocolSpecBuilder<C>> protocolSpecAdapter) {
this.config = config;
this.protocolSpecAdapter = protocolSpecAdapter;
this.defaultChainId = defaultChainId;
}
public ProtocolSchedule<C> createProtocolSchedule() {
final int chainId = config.getChainId().orElse(defaultChainId);
final MutableProtocolSchedule<C> protocolSchedule = new MutableProtocolSchedule<>(chainId);
addProtocolSpec(
protocolSchedule, OptionalLong.of(0), MainnetProtocolSpecs.frontierDefinition());
addProtocolSpec(
protocolSchedule,
config.getHomesteadBlockNumber(),
MainnetProtocolSpecs.homesteadDefinition());
config
.getDaoForkBlock()
.ifPresent(
daoBlockNumber -> {
if (daoBlockNumber > 0) {
final ProtocolSpec<C> originalProtocolSpec =
protocolSchedule.getByBlockNumber(daoBlockNumber);
addProtocolSpec(
protocolSchedule,
OptionalLong.of(daoBlockNumber),
MainnetProtocolSpecs.daoRecoveryInitDefinition());
addProtocolSpec(
protocolSchedule,
OptionalLong.of(daoBlockNumber + 1),
MainnetProtocolSpecs.daoRecoveryTransitionDefinition());
// Return to the previous protocol spec after the dao fork has completed.
protocolSchedule.putMilestone(daoBlockNumber + 10, originalProtocolSpec);
}
});
addProtocolSpec(
protocolSchedule,
config.getTangerineWhistleBlockNumber(),
MainnetProtocolSpecs.tangerineWhistleDefinition());
addProtocolSpec(
protocolSchedule,
config.getSpuriousDragonBlockNumber(),
MainnetProtocolSpecs.spuriousDragonDefinition(chainId));
addProtocolSpec(
protocolSchedule,
config.getByzantiumBlockNumber(),
MainnetProtocolSpecs.byzantiumDefinition(chainId));
addProtocolSpec(
protocolSchedule,
config.getConstantinopleBlockNumber(),
MainnetProtocolSpecs.constantinopleDefinition(chainId));
return protocolSchedule;
}
private void addProtocolSpec(
final MutableProtocolSchedule<C> protocolSchedule,
final OptionalLong blockNumber,
final ProtocolSpecBuilder<Void> definition) {
blockNumber.ifPresent(
number ->
protocolSchedule.putMilestone(
number, protocolSpecAdapter.apply(definition).build(protocolSchedule)));
}
}

@ -13,6 +13,7 @@
package tech.pegasys.pantheon.ethereum.core;
import tech.pegasys.pantheon.config.GenesisConfigFile;
import tech.pegasys.pantheon.config.StubGenesisConfigOptions;
import tech.pegasys.pantheon.ethereum.ProtocolContext;
import tech.pegasys.pantheon.ethereum.chain.GenesisState;
import tech.pegasys.pantheon.ethereum.chain.MutableBlockchain;
@ -20,13 +21,15 @@ import tech.pegasys.pantheon.ethereum.db.DefaultMutableBlockchain;
import tech.pegasys.pantheon.ethereum.db.KeyValueStoragePrefixedKeyBlockchainStorage;
import tech.pegasys.pantheon.ethereum.db.WorldStateArchive;
import tech.pegasys.pantheon.ethereum.mainnet.MainnetBlockHashFunction;
import tech.pegasys.pantheon.ethereum.mainnet.MainnetProtocolSchedule;
import tech.pegasys.pantheon.ethereum.mainnet.ProtocolSchedule;
import tech.pegasys.pantheon.ethereum.mainnet.ProtocolScheduleBuilder;
import tech.pegasys.pantheon.ethereum.worldstate.DefaultMutableWorldState;
import tech.pegasys.pantheon.ethereum.worldstate.KeyValueStorageWorldStateStorage;
import tech.pegasys.pantheon.services.kvstore.InMemoryKeyValueStorage;
import tech.pegasys.pantheon.services.kvstore.KeyValueStorage;
import java.util.function.Function;
public class ExecutionContextTestFixture {
private final Block genesis;
@ -106,7 +109,10 @@ public class ExecutionContextTestFixture {
public ExecutionContextTestFixture build() {
if (protocolSchedule == null) {
protocolSchedule = MainnetProtocolSchedule.create(0, 0, 0, 0, 0, 0, 42);
protocolSchedule =
new ProtocolScheduleBuilder<>(
new StubGenesisConfigOptions().constantinopleBlock(0), 42, Function.identity())
.createProtocolSchedule();
}
if (keyValueStorage == null) {
keyValueStorage = new InMemoryKeyValueStorage();

@ -14,6 +14,9 @@ package tech.pegasys.pantheon.ethereum.mainnet;
import tech.pegasys.pantheon.config.GenesisConfigFile;
import java.nio.charset.StandardCharsets;
import com.google.common.io.Resources;
import io.vertx.core.json.JsonObject;
import org.assertj.core.api.Assertions;
import org.junit.Test;
@ -38,36 +41,12 @@ public class MainnetProtocolScheduleTest {
}
@Test
public void shouldReturnCorrectProtocolSpecsWhenCustomNumbersAreUsed() {
final ProtocolSchedule<Void> sched = MainnetProtocolSchedule.create(2, 3, 14, 15, 16, 18, 1);
Assertions.assertThat(sched.getByBlockNumber(1).getName()).isEqualTo("Frontier");
Assertions.assertThat(sched.getByBlockNumber(2).getName()).isEqualTo("Homestead");
Assertions.assertThat(sched.getByBlockNumber(3).getName()).isEqualTo("DaoRecoveryInit");
Assertions.assertThat(sched.getByBlockNumber(4).getName()).isEqualTo("DaoRecoveryTransition");
Assertions.assertThat(sched.getByBlockNumber(13).getName()).isEqualTo("Homestead");
Assertions.assertThat(sched.getByBlockNumber(14).getName()).isEqualTo("TangerineWhistle");
Assertions.assertThat(sched.getByBlockNumber(15).getName()).isEqualTo("SpuriousDragon");
Assertions.assertThat(sched.getByBlockNumber(16).getName()).isEqualTo("Byzantium");
Assertions.assertThat(sched.getByBlockNumber(18).getName()).isEqualTo("Constantinople");
}
@Test
public void shouldReturnDefaultProtocolSpecsWhenEmptyJsonConfigIsUsed() {
public void shouldOnlyUseFrontierWhenEmptyJsonConfigIsUsed() {
final JsonObject json = new JsonObject("{}");
final ProtocolSchedule<Void> sched =
MainnetProtocolSchedule.fromConfig(GenesisConfigFile.fromConfig(json).getConfigOptions());
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())
.isEqualTo("DaoRecoveryInit");
Assertions.assertThat(sched.getByBlockNumber(1_920_001L).getName())
.isEqualTo("DaoRecoveryTransition");
Assertions.assertThat(sched.getByBlockNumber(1_920_010L).getName()).isEqualTo("Homestead");
Assertions.assertThat(sched.getByBlockNumber(2_463_000L).getName())
.isEqualTo("TangerineWhistle");
Assertions.assertThat(sched.getByBlockNumber(2_675_000L).getName()).isEqualTo("SpuriousDragon");
Assertions.assertThat(sched.getByBlockNumber(4_730_000L).getName()).isEqualTo("Byzantium");
Assertions.assertThat(sched.getByBlockNumber(Long.MAX_VALUE).getName()).isEqualTo("Byzantium");
Assertions.assertThat(sched.getByBlockNumber(Long.MAX_VALUE).getName()).isEqualTo("Frontier");
}
@Test
@ -89,13 +68,19 @@ public class MainnetProtocolScheduleTest {
}
@Test
public void shouldCreateRopstenConfig() {
public void shouldCreateRopstenConfig() throws Exception {
final ProtocolSchedule<Void> sched =
MainnetProtocolSchedule.create(0, 0, 0, 10, 1700000, -1, 3);
MainnetProtocolSchedule.fromConfig(
GenesisConfigFile.fromConfig(
Resources.toString(
Resources.getResource("ropsten.json"), StandardCharsets.UTF_8))
.getConfigOptions());
Assertions.assertThat(sched.getByBlockNumber(0).getName()).isEqualTo("TangerineWhistle");
Assertions.assertThat(sched.getByBlockNumber(1).getName()).isEqualTo("TangerineWhistle");
Assertions.assertThat(sched.getByBlockNumber(10).getName()).isEqualTo("SpuriousDragon");
Assertions.assertThat(sched.getByBlockNumber(1700000).getName()).isEqualTo("Byzantium");
Assertions.assertThat(sched.getByBlockNumber(Long.MAX_VALUE).getName()).isEqualTo("Byzantium");
Assertions.assertThat(sched.getByBlockNumber(4230000).getName()).isEqualTo("Constantinople");
Assertions.assertThat(sched.getByBlockNumber(Long.MAX_VALUE).getName())
.isEqualTo("Constantinople");
}
}

@ -12,10 +12,10 @@
*/
package tech.pegasys.pantheon.ethereum.vm;
import tech.pegasys.pantheon.ethereum.mainnet.MainnetProtocolSpecs;
import tech.pegasys.pantheon.ethereum.mainnet.MutableProtocolSchedule;
import tech.pegasys.pantheon.config.GenesisConfigOptions;
import tech.pegasys.pantheon.config.StubGenesisConfigOptions;
import tech.pegasys.pantheon.ethereum.mainnet.ProtocolSchedule;
import tech.pegasys.pantheon.ethereum.mainnet.ProtocolSpec;
import tech.pegasys.pantheon.ethereum.mainnet.ProtocolScheduleBuilder;
import java.util.Map;
import java.util.function.Function;
@ -28,26 +28,24 @@ public class ReferenceTestProtocolSchedules {
public static ReferenceTestProtocolSchedules create() {
final ImmutableMap.Builder<String, ProtocolSchedule<Void>> builder = ImmutableMap.builder();
builder.put("Frontier", createSchedule(MainnetProtocolSpecs::frontier));
builder.put("FrontierToHomesteadAt5", frontierToHomesteadAt5());
builder.put("Homestead", createSchedule(MainnetProtocolSpecs::homestead));
builder.put("HomesteadToEIP150At5", homesteadToEip150At5());
builder.put("HomesteadToDaoAt5", homesteadToDaoAt5());
builder.put("EIP150", createSchedule(MainnetProtocolSpecs::tangerineWhistle));
builder.put("Frontier", createSchedule(new StubGenesisConfigOptions()));
builder.put(
"EIP158",
createSchedule(
protocolSpecLookup ->
MainnetProtocolSpecs.spuriousDragon(CHAIN_ID, protocolSpecLookup)));
builder.put("EIP158ToByzantiumAt5", eip158ToByzantiumAt5());
"FrontierToHomesteadAt5", createSchedule(new StubGenesisConfigOptions().homesteadBlock(5)));
builder.put("Homestead", createSchedule(new StubGenesisConfigOptions().homesteadBlock(0)));
builder.put(
"Byzantium",
createSchedule(
protocolSchedule -> MainnetProtocolSpecs.byzantium(CHAIN_ID, protocolSchedule)));
"HomesteadToEIP150At5",
createSchedule(new StubGenesisConfigOptions().homesteadBlock(0).eip150Block(5)));
builder.put(
"Constantinople",
createSchedule(
protocolSchedule -> MainnetProtocolSpecs.constantinople(CHAIN_ID, protocolSchedule)));
"HomesteadToDaoAt5",
createSchedule(new StubGenesisConfigOptions().homesteadBlock(0).daoForkBlock(5)));
builder.put("EIP150", createSchedule(new StubGenesisConfigOptions().eip150Block(0)));
builder.put("EIP158", createSchedule(new StubGenesisConfigOptions().eip158Block(0)));
builder.put(
"EIP158ToByzantiumAt5",
createSchedule(new StubGenesisConfigOptions().eip158Block(0).byzantiumBlock(5)));
builder.put("Byzantium", createSchedule(new StubGenesisConfigOptions().byzantiumBlock(0)));
builder.put(
"Constantinople", createSchedule(new StubGenesisConfigOptions().constantinopleBlock(0)));
return new ReferenceTestProtocolSchedules(builder.build());
}
@ -61,42 +59,8 @@ public class ReferenceTestProtocolSchedules {
return schedules.get(name);
}
private static ProtocolSchedule<Void> createSchedule(
final Function<ProtocolSchedule<Void>, ProtocolSpec<Void>> specCreator) {
final MutableProtocolSchedule<Void> protocolSchedule = new MutableProtocolSchedule<>(CHAIN_ID);
protocolSchedule.putMilestone(0, specCreator.apply(protocolSchedule));
return protocolSchedule;
}
private static ProtocolSchedule<Void> frontierToHomesteadAt5() {
final MutableProtocolSchedule<Void> protocolSchedule = new MutableProtocolSchedule<>(CHAIN_ID);
protocolSchedule.putMilestone(0, MainnetProtocolSpecs.frontier(protocolSchedule));
protocolSchedule.putMilestone(5, MainnetProtocolSpecs.homestead(protocolSchedule));
return protocolSchedule;
}
private static ProtocolSchedule<Void> homesteadToEip150At5() {
final MutableProtocolSchedule<Void> protocolSchedule = new MutableProtocolSchedule<>(CHAIN_ID);
protocolSchedule.putMilestone(0, MainnetProtocolSpecs.homestead(protocolSchedule));
protocolSchedule.putMilestone(5, MainnetProtocolSpecs.tangerineWhistle(protocolSchedule));
return protocolSchedule;
}
private static ProtocolSchedule<Void> homesteadToDaoAt5() {
final MutableProtocolSchedule<Void> protocolSchedule = new MutableProtocolSchedule<>(CHAIN_ID);
final ProtocolSpec<Void> homestead = MainnetProtocolSpecs.homestead(protocolSchedule);
protocolSchedule.putMilestone(0, homestead);
protocolSchedule.putMilestone(5, MainnetProtocolSpecs.daoRecoveryInit(protocolSchedule));
protocolSchedule.putMilestone(6, MainnetProtocolSpecs.daoRecoveryTransition(protocolSchedule));
protocolSchedule.putMilestone(15, homestead);
return protocolSchedule;
}
private static ProtocolSchedule<Void> eip158ToByzantiumAt5() {
final MutableProtocolSchedule<Void> protocolSchedule = new MutableProtocolSchedule<>(CHAIN_ID);
protocolSchedule.putMilestone(
0, MainnetProtocolSpecs.spuriousDragon(CHAIN_ID, protocolSchedule));
protocolSchedule.putMilestone(5, MainnetProtocolSpecs.byzantium(CHAIN_ID, protocolSchedule));
return protocolSchedule;
private static ProtocolSchedule<Void> createSchedule(final GenesisConfigOptions options) {
return new ProtocolScheduleBuilder<>(options, CHAIN_ID, Function.identity())
.createProtocolSchedule();
}
}

@ -115,7 +115,7 @@ public class VMReferenceTest extends AbstractRetryingTest {
final EnvironmentInformation execEnv = spec.getExec();
final ProtocolSpec<Void> protocolSpec =
MainnetProtocolSpecs.frontier(new MutableProtocolSchedule<>(CHAIN_ID));
MainnetProtocolSpecs.frontierDefinition().build(new MutableProtocolSchedule<>(CHAIN_ID));
final TestBlockchain blockchain = new TestBlockchain(execEnv.getBlockHeader().getNumber());
final MessageFrame frame =

@ -14,6 +14,7 @@ package tech.pegasys.pantheon.ethereum.vm.operations;
import static org.assertj.core.api.Assertions.assertThat;
import tech.pegasys.pantheon.config.StubGenesisConfigOptions;
import tech.pegasys.pantheon.ethereum.core.Gas;
import tech.pegasys.pantheon.ethereum.core.TestCodeExecutor;
import tech.pegasys.pantheon.ethereum.mainnet.MainnetProtocolSchedule;
@ -31,8 +32,9 @@ import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
public class ConstantinopleSStoreOperationGasCostTest {
private static final ProtocolSchedule<Void> protocolSchedule =
MainnetProtocolSchedule.create(0, 0, 0, 0, 0, 0, 1);
MainnetProtocolSchedule.fromConfig(new StubGenesisConfigOptions().constantinopleBlock(0));
@Parameters(name = "Code: {0}, Original: {1}")
public static Object[][] scenarios() {

@ -35,6 +35,8 @@ dependencies {
testImplementation project(path: ':ethereum:core', configuration: 'testArtifacts')
testImplementation project(path: ':ethereum:core', configuration: 'testSupportArtifacts')
testImplementation project(':config')
testImplementation project(path: ':config', configuration: 'testSupportArtifacts')
testImplementation project(':services:kvstore')
testImplementation project(':testutil')
@ -46,6 +48,7 @@ dependencies {
testImplementation 'io.vertx:vertx-unit'
integrationTestImplementation project(':config')
integrationTestImplementation project(path: ':config', configuration: 'testSupportArtifacts')
integrationTestImplementation project(path: ':ethereum:core', configuration: 'testSupportArtifacts')
integrationTestImplementation project(':services:kvstore')

@ -19,6 +19,7 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import tech.pegasys.pantheon.config.StubGenesisConfigOptions;
import tech.pegasys.pantheon.ethereum.blockcreation.EthHashMiningCoordinator;
import tech.pegasys.pantheon.ethereum.core.Address;
import tech.pegasys.pantheon.ethereum.core.Block;
@ -113,7 +114,8 @@ public class JsonRpcHttpServiceTest {
peerDiscoveryMock,
blockchainQueries,
synchronizer,
MainnetProtocolSchedule.create(0, 0, 0, 0, 0, 0, CHAIN_ID),
MainnetProtocolSchedule.fromConfig(
new StubGenesisConfigOptions().constantinopleBlock(0).chainId(CHAIN_ID)),
mock(FilterManager.class),
mock(TransactionPool.class),
mock(EthHashMiningCoordinator.class),

@ -99,10 +99,11 @@ public class MainnetPantheonController implements PantheonController<Void> {
public static PantheonController<Void> mainnet(final Path home) throws IOException {
final MiningParameters miningParams = new MiningParameters(null, null, null, false);
final KeyPair nodeKeys = loadKeyPair(home);
final GenesisConfigFile genesisConfig = GenesisConfigFile.mainnet();
return init(
home,
GenesisConfigFile.mainnet(),
MainnetProtocolSchedule.create(),
genesisConfig,
MainnetProtocolSchedule.fromConfig(genesisConfig.getConfigOptions()),
SynchronizerConfiguration.builder().build(),
miningParams,
nodeKeys);

Loading…
Cancel
Save