From 0e3f199ef59026ca3554ce564f2e42a87b407450 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Wed, 23 Jan 2019 23:16:06 -0700 Subject: [PATCH] NC-2147 Implement ConstantinopleFix hardfork (#601) * create a new fork definition * make it constantinople without EIP-1283 * update tests where appropriate * Add PetersbergGasCalculator * Add fork ordering test * Add rinkeby Constantinople block * fix json config bug * add Ropsten constantinopleFix block * run reference tests * clique test genesis file violates block ordering rules Signed-off-by: Adrian Sutton --- .../src/test/resources/clique/clique.json | 8 +- .../pantheon/config/GenesisConfigOptions.java | 2 + .../config/JsonGenesisConfigOptions.java | 5 + config/src/main/resources/dev.json | 1 + config/src/main/resources/goerli.json | 1 + config/src/main/resources/mainnet.json | 2 + config/src/main/resources/rinkeby.json | 1 + config/src/main/resources/ropsten.json | 1 + .../config/StubGenesisConfigOptions.java | 11 ++ .../config/GenesisConfigOptionsTest.java | 8 ++ .../BlockHashOperationBenchmark.java | 4 +- .../ConstantinopleFixGasCalculator.java | 56 +++++++++ .../mainnet/MainnetProtocolSpecs.java | 6 + .../mainnet/ProtocolScheduleBuilder.java | 36 ++++++ .../core/ExecutionContextTestFixture.java | 2 +- .../core/TransactionTestCaseSpec.java | 2 +- .../ConstantinopleFixSstoreGasTest.java | 110 ++++++++++++++++++ .../mainnet/MainnetProtocolScheduleTest.java | 26 ++++- .../vm/BlockchainReferenceTestTools.java | 5 +- .../vm/GeneralStateReferenceTestTools.java | 4 +- .../vm/ReferenceTestProtocolSchedules.java | 3 + 21 files changed, 280 insertions(+), 14 deletions(-) create mode 100644 ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/ConstantinopleFixGasCalculator.java create mode 100644 ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/mainnet/ConstantinopleFixSstoreGasTest.java diff --git a/acceptance-tests/src/test/resources/clique/clique.json b/acceptance-tests/src/test/resources/clique/clique.json index 66146e0483..971ccb4041 100644 --- a/acceptance-tests/src/test/resources/clique/clique.json +++ b/acceptance-tests/src/test/resources/clique/clique.json @@ -4,9 +4,11 @@ "homesteadBlock": 1, "eip150Block": 2, "eip150Hash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "eip155Block": 0, - "eip158Block": 0, - "byzantiumBlock": 0, + "eip155Block": 3, + "eip158Block": 4, + "byzantiumBlock": 5, + "constantinopleBlock": 6, + "constantinopleFixBlock": 7, "clique": { "blockperiodseconds": 10, "epochlength": 30000 diff --git a/config/src/main/java/tech/pegasys/pantheon/config/GenesisConfigOptions.java b/config/src/main/java/tech/pegasys/pantheon/config/GenesisConfigOptions.java index 8141836faa..eb2e3b04fc 100644 --- a/config/src/main/java/tech/pegasys/pantheon/config/GenesisConfigOptions.java +++ b/config/src/main/java/tech/pegasys/pantheon/config/GenesisConfigOptions.java @@ -43,5 +43,7 @@ public interface GenesisConfigOptions { OptionalLong getConstantinopleBlockNumber(); + OptionalLong getConstantinopleFixBlockNumber(); + OptionalInt getChainId(); } diff --git a/config/src/main/java/tech/pegasys/pantheon/config/JsonGenesisConfigOptions.java b/config/src/main/java/tech/pegasys/pantheon/config/JsonGenesisConfigOptions.java index 8bc1cb4866..7ec0f1c8a7 100644 --- a/config/src/main/java/tech/pegasys/pantheon/config/JsonGenesisConfigOptions.java +++ b/config/src/main/java/tech/pegasys/pantheon/config/JsonGenesisConfigOptions.java @@ -100,6 +100,11 @@ public class JsonGenesisConfigOptions implements GenesisConfigOptions { return getOptionalLong("constantinopleblock"); } + @Override + public OptionalLong getConstantinopleFixBlockNumber() { + return getOptionalLong("constantinoplefixblock"); + } + @Override public OptionalInt getChainId() { return configRoot.containsKey("chainid") diff --git a/config/src/main/resources/dev.json b/config/src/main/resources/dev.json index 0239cff435..f515cd9cad 100644 --- a/config/src/main/resources/dev.json +++ b/config/src/main/resources/dev.json @@ -8,6 +8,7 @@ "eip158Block": 0, "byzantiumBlock": 0, "constantinopleBlock": 0, + "constantinopleFixBlock": 0, "ethash": { } }, diff --git a/config/src/main/resources/goerli.json b/config/src/main/resources/goerli.json index 711883da4f..f8fa6ed508 100644 --- a/config/src/main/resources/goerli.json +++ b/config/src/main/resources/goerli.json @@ -9,6 +9,7 @@ "eip160Block":0, "byzantiumBlock":0, "constantinopleBlock":0, + "constantinopleFixBlock": 0, "clique":{ "period":15, "epoch":30000 diff --git a/config/src/main/resources/mainnet.json b/config/src/main/resources/mainnet.json index b853e7bcf6..925b6e575c 100644 --- a/config/src/main/resources/mainnet.json +++ b/config/src/main/resources/mainnet.json @@ -9,6 +9,8 @@ "eip155Block": 2675000, "eip158Block": 2675000, "byzantiumBlock": 4370000, + "constantinopleBlock": 7280000, + "constantinopleFixBlock": 7280000, "ethash": { } diff --git a/config/src/main/resources/rinkeby.json b/config/src/main/resources/rinkeby.json index 1508923164..8aace7b247 100644 --- a/config/src/main/resources/rinkeby.json +++ b/config/src/main/resources/rinkeby.json @@ -7,6 +7,7 @@ "eip155Block": 3, "eip158Block": 3, "byzantiumBlock": 1035301, + "constantinopleBlock": 3660663, "clique": { "period": 15, "epoch": 30000 diff --git a/config/src/main/resources/ropsten.json b/config/src/main/resources/ropsten.json index fbf5e3f5b3..710f15acc9 100644 --- a/config/src/main/resources/ropsten.json +++ b/config/src/main/resources/ropsten.json @@ -7,6 +7,7 @@ "eip158Block": 10, "byzantiumBlock": 1700000, "constantinopleBlock": 4230000, + "constantinopleFixBlock": 4939394, "ethash": { } }, diff --git a/config/src/test-support/java/tech/pegasys/pantheon/config/StubGenesisConfigOptions.java b/config/src/test-support/java/tech/pegasys/pantheon/config/StubGenesisConfigOptions.java index 3e7788efce..9748b52950 100644 --- a/config/src/test-support/java/tech/pegasys/pantheon/config/StubGenesisConfigOptions.java +++ b/config/src/test-support/java/tech/pegasys/pantheon/config/StubGenesisConfigOptions.java @@ -23,6 +23,7 @@ public class StubGenesisConfigOptions implements GenesisConfigOptions { private OptionalLong spuriousDragonBlockNumber = OptionalLong.empty(); private OptionalLong byzantiumBlockNumber = OptionalLong.empty(); private OptionalLong constantinopleBlockNumber = OptionalLong.empty(); + private OptionalLong constantinopleFixBlockNumber = OptionalLong.empty(); private OptionalInt chainId = OptionalInt.empty(); @Override @@ -90,6 +91,11 @@ public class StubGenesisConfigOptions implements GenesisConfigOptions { return constantinopleBlockNumber; } + @Override + public OptionalLong getConstantinopleFixBlockNumber() { + return constantinopleFixBlockNumber; + } + @Override public OptionalInt getChainId() { return chainId; @@ -125,6 +131,11 @@ public class StubGenesisConfigOptions implements GenesisConfigOptions { return this; } + public StubGenesisConfigOptions constantinopleFixBlock(final long blockNumber) { + constantinopleFixBlockNumber = OptionalLong.of(blockNumber); + return this; + } + public StubGenesisConfigOptions chainId(final int chainId) { this.chainId = OptionalInt.of(chainId); return this; diff --git a/config/src/test/java/tech/pegasys/pantheon/config/GenesisConfigOptionsTest.java b/config/src/test/java/tech/pegasys/pantheon/config/GenesisConfigOptionsTest.java index c4a385b934..a7fac49800 100644 --- a/config/src/test/java/tech/pegasys/pantheon/config/GenesisConfigOptionsTest.java +++ b/config/src/test/java/tech/pegasys/pantheon/config/GenesisConfigOptionsTest.java @@ -101,6 +101,13 @@ public class GenesisConfigOptionsTest { assertThat(config.getConstantinopleBlockNumber()).hasValue(1000); } + @Test + public void shouldGetConstantinopleFixBlockNumber() { + final GenesisConfigOptions config = + fromConfigOptions(singletonMap("constantinopleFixBlock", 1000)); + assertThat(config.getConstantinopleFixBlockNumber()).hasValue(1000); + } + @Test public void shouldNotReturnEmptyOptionalWhenBlockNumberNotSpecified() { final GenesisConfigOptions config = fromConfigOptions(emptyMap()); @@ -110,6 +117,7 @@ public class GenesisConfigOptionsTest { assertThat(config.getSpuriousDragonBlockNumber()).isEmpty(); assertThat(config.getByzantiumBlockNumber()).isEmpty(); assertThat(config.getConstantinopleBlockNumber()).isEmpty(); + assertThat(config.getConstantinopleFixBlockNumber()).isEmpty(); } @Test diff --git a/ethereum/core/src/jmh/java/tech/pegasys/pantheon/ethereum/vm/operations/BlockHashOperationBenchmark.java b/ethereum/core/src/jmh/java/tech/pegasys/pantheon/ethereum/vm/operations/BlockHashOperationBenchmark.java index 7250837953..8fb6658efd 100644 --- a/ethereum/core/src/jmh/java/tech/pegasys/pantheon/ethereum/vm/operations/BlockHashOperationBenchmark.java +++ b/ethereum/core/src/jmh/java/tech/pegasys/pantheon/ethereum/vm/operations/BlockHashOperationBenchmark.java @@ -12,7 +12,7 @@ */ package tech.pegasys.pantheon.ethereum.vm.operations; -import tech.pegasys.pantheon.ethereum.mainnet.ConstantinopleGasCalculator; +import tech.pegasys.pantheon.ethereum.mainnet.ConstantinopleFixGasCalculator; import tech.pegasys.pantheon.ethereum.vm.BlockHashLookup; import tech.pegasys.pantheon.ethereum.vm.MessageFrame; import tech.pegasys.pantheon.util.bytes.Bytes32; @@ -42,7 +42,7 @@ public class BlockHashOperationBenchmark { @Setup public void prepare() throws Exception { operationBenchmarkHelper = OperationBenchmarkHelper.create(); - operation = new BlockHashOperation(new ConstantinopleGasCalculator()); + operation = new BlockHashOperation(new ConstantinopleFixGasCalculator()); frame = operationBenchmarkHelper.createMessageFrame(); } diff --git a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/ConstantinopleFixGasCalculator.java b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/ConstantinopleFixGasCalculator.java new file mode 100644 index 0000000000..034d84bce7 --- /dev/null +++ b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/ConstantinopleFixGasCalculator.java @@ -0,0 +1,56 @@ +/* + * 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.ethereum.core.Account; +import tech.pegasys.pantheon.ethereum.core.Gas; +import tech.pegasys.pantheon.util.uint.UInt256; + +/** + * Gas Calculator for Petersberg Hard Fork. Rollback EIP-1283. + * + *

Neither {@link TangerineWhistleGasCalculator} nor {@link SpuriousDragonGasCalculator} overrode + * these two methods so {@link FrontierGasCalculator} is the source. + */ +public class ConstantinopleFixGasCalculator extends ConstantinopleGasCalculator { + + /** Same as {#link {@link FrontierGasCalculator#STORAGE_SET_GAS_COST} */ + private static final Gas STORAGE_SET_GAS_COST = Gas.of(20_000L); + /** Same as {#link {@link FrontierGasCalculator#STORAGE_RESET_GAS_COST} */ + private static final Gas STORAGE_RESET_GAS_COST = Gas.of(5_000L); + /** Same as {#link {@link FrontierGasCalculator#STORAGE_RESET_REFUND_AMOUNT} */ + private static final Gas STORAGE_RESET_REFUND_AMOUNT = Gas.of(15_000L); + + /** + * Same as {#link {@link FrontierGasCalculator#calculateStorageCost(Account, UInt256, UInt256)} + */ + @Override + public Gas calculateStorageCost( + final Account account, final UInt256 key, final UInt256 newValue) { + return !newValue.isZero() && account.getStorageValue(key).isZero() + ? STORAGE_SET_GAS_COST + : STORAGE_RESET_GAS_COST; + } + + /** + * Same as {#link {@link FrontierGasCalculator#calculateStorageRefundAmount(Account, UInt256, + * UInt256)} + */ + @Override + public Gas calculateStorageRefundAmount( + final Account account, final UInt256 key, final UInt256 newValue) { + return newValue.isZero() && !account.getStorageValue(key).isZero() + ? STORAGE_RESET_REFUND_AMOUNT + : Gas.ZERO; + } +} diff --git a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/MainnetProtocolSpecs.java b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/MainnetProtocolSpecs.java index 6941e02ce6..36da2eee28 100644 --- a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/MainnetProtocolSpecs.java +++ b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/MainnetProtocolSpecs.java @@ -193,6 +193,12 @@ public abstract class MainnetProtocolSpecs { .name("Constantinople"); } + public static ProtocolSpecBuilder constantinopleFixDefinition(final int chainId) { + return constantinopleDefinition(chainId) + .gasCalculator(ConstantinopleFixGasCalculator::new) + .name("ConstantinopleFix"); + } + private static TransactionReceipt frontierTransactionReceiptFactory( final TransactionProcessor.Result result, final WorldState worldState, final long gasUsed) { return new TransactionReceipt(worldState.rootHash(), gasUsed, result.getLogs()); diff --git a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/ProtocolScheduleBuilder.java b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/ProtocolScheduleBuilder.java index 979625d42f..8c6ea64a8e 100644 --- a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/ProtocolScheduleBuilder.java +++ b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/ProtocolScheduleBuilder.java @@ -40,6 +40,8 @@ public class ProtocolScheduleBuilder { final int chainId = config.getChainId().orElse(defaultChainId); final MutableProtocolSchedule protocolSchedule = new MutableProtocolSchedule<>(chainId); + validateForkOrdering(); + addProtocolSpec( protocolSchedule, OptionalLong.of(0), MainnetProtocolSpecs.frontierDefinition()); addProtocolSpec( @@ -84,6 +86,10 @@ public class ProtocolScheduleBuilder { protocolSchedule, config.getConstantinopleBlockNumber(), MainnetProtocolSpecs.constantinopleDefinition(chainId)); + addProtocolSpec( + protocolSchedule, + config.getConstantinopleFixBlockNumber(), + MainnetProtocolSpecs.constantinopleFixDefinition(chainId)); return protocolSchedule; } @@ -101,4 +107,34 @@ public class ProtocolScheduleBuilder { .privacyParameters(privacyParameters) .build(protocolSchedule))); } + + private long validateForkOrder( + final String forkName, final OptionalLong thisForkBlock, final long lastForkBlock) { + final long referenceForkBlock = thisForkBlock.orElse(lastForkBlock); + if (lastForkBlock > referenceForkBlock) { + throw new RuntimeException( + String.format( + "Genesis Config Error: '%s' is scheduled for block %d but it must be on or after block %d.", + forkName, thisForkBlock.getAsLong(), lastForkBlock)); + } + return referenceForkBlock; + } + + private void validateForkOrdering() { + long lastForkBlock = 0; + lastForkBlock = validateForkOrder("Homestead", config.getHomesteadBlockNumber(), lastForkBlock); + lastForkBlock = validateForkOrder("DaoFork", config.getDaoForkBlock(), lastForkBlock); + lastForkBlock = + validateForkOrder( + "TangerineWhistle", config.getTangerineWhistleBlockNumber(), lastForkBlock); + lastForkBlock = + validateForkOrder("SpuriousDragon", config.getSpuriousDragonBlockNumber(), lastForkBlock); + lastForkBlock = validateForkOrder("Byzantium", config.getByzantiumBlockNumber(), lastForkBlock); + lastForkBlock = + validateForkOrder("Constantinople", config.getConstantinopleBlockNumber(), lastForkBlock); + lastForkBlock = + validateForkOrder( + "ConstantinopleFix", config.getConstantinopleFixBlockNumber(), lastForkBlock); + assert (lastForkBlock >= 0); + } } diff --git a/ethereum/core/src/test-support/java/tech/pegasys/pantheon/ethereum/core/ExecutionContextTestFixture.java b/ethereum/core/src/test-support/java/tech/pegasys/pantheon/ethereum/core/ExecutionContextTestFixture.java index 21aa844f6c..701b66f8c0 100644 --- a/ethereum/core/src/test-support/java/tech/pegasys/pantheon/ethereum/core/ExecutionContextTestFixture.java +++ b/ethereum/core/src/test-support/java/tech/pegasys/pantheon/ethereum/core/ExecutionContextTestFixture.java @@ -113,7 +113,7 @@ public class ExecutionContextTestFixture { if (protocolSchedule == null) { protocolSchedule = new ProtocolScheduleBuilder<>( - new StubGenesisConfigOptions().constantinopleBlock(0), + new StubGenesisConfigOptions().constantinopleFixBlock(0), 42, Function.identity(), PrivacyParameters.noPrivacy()) diff --git a/ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/core/TransactionTestCaseSpec.java b/ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/core/TransactionTestCaseSpec.java index 362bd4f4ef..43e7647535 100644 --- a/ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/core/TransactionTestCaseSpec.java +++ b/ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/core/TransactionTestCaseSpec.java @@ -96,7 +96,7 @@ public class TransactionTestCaseSpec { final Expectation expectation = expectations.get(milestone); if (expectation == null) { - throw new IllegalStateException("Expectation for milestone %s not found" + milestone); + throw new IllegalStateException("Expectation for milestone " + milestone + " not found"); } return expectation; diff --git a/ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/mainnet/ConstantinopleFixSstoreGasTest.java b/ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/mainnet/ConstantinopleFixSstoreGasTest.java new file mode 100644 index 0000000000..d75d49cbd4 --- /dev/null +++ b/ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/mainnet/ConstantinopleFixSstoreGasTest.java @@ -0,0 +1,110 @@ +/* + * 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 static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static tech.pegasys.pantheon.util.uint.UInt256.ONE; +import static tech.pegasys.pantheon.util.uint.UInt256.ZERO; + +import tech.pegasys.pantheon.ethereum.core.Account; +import tech.pegasys.pantheon.ethereum.core.Gas; +import tech.pegasys.pantheon.util.uint.UInt256; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; +import org.junit.runners.Parameterized.Parameters; + +@RunWith(Parameterized.class) +public class ConstantinopleFixSstoreGasTest { + + private static final UInt256 TWO = UInt256.of(2); + + private final ConstantinopleFixGasCalculator gasCalculator = new ConstantinopleFixGasCalculator(); + + @Parameters(name = "original: {0}, current: {1}, new: {2}") + public static Object[][] scenarios() { + return new Object[][] { + // Zero no-op + {ZERO, ZERO, ZERO, Gas.of(5_000), Gas.ZERO}, + + // Zero fresh change + {ZERO, ZERO, ONE, Gas.of(20_000), Gas.ZERO}, + + // Dirty, reset to zero + {ZERO, ONE, ZERO, Gas.of(5_000), Gas.of(15_000)}, + + // Dirty, changed but not reset + {ZERO, ONE, TWO, Gas.of(5_000), Gas.ZERO}, + + // Dirty no-op + {ZERO, ONE, ONE, Gas.of(5_000), Gas.ZERO}, + + // Dirty, zero no-op + {ONE, ZERO, ZERO, Gas.of(5_000), Gas.ZERO}, + + // Dirty, reset to non-zero + {ONE, ZERO, ONE, Gas.of(20_000), Gas.ZERO}, + + // Fresh change to zero + {ONE, ONE, ZERO, Gas.of(5_000), Gas.of(15_000)}, + + // Fresh change with all non-zero + {ONE, ONE, TWO, Gas.of(5_000), Gas.ZERO}, + + // Dirty, clear originally set value + {ONE, TWO, ZERO, Gas.of(5_000), Gas.of(15_000)}, + + // Non-zero no-op + {ONE, ONE, ONE, Gas.of(5_000), Gas.ZERO}, + }; + } + + @Parameter public UInt256 originalValue; + + @Parameter(value = 1) + public UInt256 currentValue; + + @Parameter(value = 2) + public UInt256 newValue; + + @Parameter(value = 3) + public Gas expectedGasCost; + + @Parameter(value = 4) + public Gas expectedGasRefund; + + private final Account account = mock(Account.class); + + @Before + public void setUp() { + when(account.getOriginalStorageValue(UInt256.ZERO)).thenReturn(originalValue); + when(account.getStorageValue(UInt256.ZERO)).thenReturn(currentValue); + } + + @Test + public void shouldChargeCorrectGas() { + assertThat(gasCalculator.calculateStorageCost(account, UInt256.ZERO, newValue)) + .isEqualTo(expectedGasCost); + } + + @Test + public void shouldRefundCorrectGas() { + assertThat(gasCalculator.calculateStorageRefundAmount(account, UInt256.ZERO, newValue)) + .isEqualTo(expectedGasRefund); + } +} diff --git a/ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/mainnet/MainnetProtocolScheduleTest.java b/ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/mainnet/MainnetProtocolScheduleTest.java index faeec8b6a9..788109c1fc 100644 --- a/ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/mainnet/MainnetProtocolScheduleTest.java +++ b/ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/mainnet/MainnetProtocolScheduleTest.java @@ -40,7 +40,10 @@ public class MainnetProtocolScheduleTest { Assertions.assertThat(sched.getByBlockNumber(4_730_000L).getName()).isEqualTo("Byzantium"); // Constantinople was originally scheduled for 7_080_000, but postponed Assertions.assertThat(sched.getByBlockNumber(7_080_000L).getName()).isEqualTo("Byzantium"); - Assertions.assertThat(sched.getByBlockNumber(Long.MAX_VALUE).getName()).isEqualTo("Byzantium"); + Assertions.assertThat(sched.getByBlockNumber(7_280_000L).getName()) + .isEqualTo("ConstantinopleFix"); + Assertions.assertThat(sched.getByBlockNumber(Long.MAX_VALUE).getName()) + .isEqualTo("ConstantinopleFix"); } @Test @@ -57,7 +60,7 @@ public class MainnetProtocolScheduleTest { public void createFromConfigWithSettings() { final JsonObject json = new JsonObject( - "{\"config\": {\"homesteadBlock\": 2, \"daoForkBlock\": 3, \"eip150Block\": 14, \"eip158Block\": 15, \"byzantiumBlock\": 16, \"constantinopleBlock\": 18, \"chainId\":1234}}"); + "{\"config\": {\"homesteadBlock\": 2, \"daoForkBlock\": 3, \"eip150Block\": 14, \"eip158Block\": 15, \"byzantiumBlock\": 16, \"constantinopleBlock\": 18, \"constantinopleFixBlock\": 19, \"chainId\":1234}}"); final ProtocolSchedule sched = MainnetProtocolSchedule.fromConfig( GenesisConfigFile.fromConfig(json).getConfigOptions(), PrivacyParameters.noPrivacy()); @@ -70,6 +73,22 @@ public class MainnetProtocolScheduleTest { Assertions.assertThat(sched.getByBlockNumber(15).getName()).isEqualTo("SpuriousDragon"); Assertions.assertThat(sched.getByBlockNumber(16).getName()).isEqualTo("Byzantium"); Assertions.assertThat(sched.getByBlockNumber(18).getName()).isEqualTo("Constantinople"); + Assertions.assertThat(sched.getByBlockNumber(19).getName()).isEqualTo("ConstantinopleFix"); + } + + @Test + public void outOfOrderForksFails() { + final JsonObject json = + new JsonObject( + "{\"config\": {\"homesteadBlock\": 2, \"daoForkBlock\": 3, \"eip150Block\": 14, \"eip158Block\": 15, \"byzantiumBlock\": 16, \"constantinopleBlock\": 18, \"constantinopleFixBlock\": 17, \"chainId\":1234}}"); + Assertions.assertThatExceptionOfType(RuntimeException.class) + .describedAs( + "Genesis Config Error: 'ConstantinopleFix' is scheduled for block 17 but it must be on or after block 18.") + .isThrownBy( + () -> + MainnetProtocolSchedule.fromConfig( + GenesisConfigFile.fromConfig(json).getConfigOptions(), + PrivacyParameters.noPrivacy())); } @Test @@ -86,7 +105,8 @@ public class MainnetProtocolScheduleTest { Assertions.assertThat(sched.getByBlockNumber(10).getName()).isEqualTo("SpuriousDragon"); Assertions.assertThat(sched.getByBlockNumber(1700000).getName()).isEqualTo("Byzantium"); Assertions.assertThat(sched.getByBlockNumber(4230000).getName()).isEqualTo("Constantinople"); + Assertions.assertThat(sched.getByBlockNumber(4939394).getName()).isEqualTo("ConstantinopleFix"); Assertions.assertThat(sched.getByBlockNumber(Long.MAX_VALUE).getName()) - .isEqualTo("Constantinople"); + .isEqualTo("ConstantinopleFix"); } } diff --git a/ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/vm/BlockchainReferenceTestTools.java b/ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/vm/BlockchainReferenceTestTools.java index 12b4d6ffbf..1e2ccb281a 100644 --- a/ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/vm/BlockchainReferenceTestTools.java +++ b/ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/vm/BlockchainReferenceTestTools.java @@ -43,7 +43,7 @@ public class BlockchainReferenceTestTools { System.getProperty( "test.ethereum.blockchain.eips", "FrontierToHomesteadAt5,HomesteadToEIP150At5,HomesteadToDaoAt5,EIP158ToByzantiumAt5," - + "Frontier,Homestead,EIP150,EIP158,Byzantium,Constantinople"); + + "Frontier,Homestead,EIP150,EIP158,Byzantium,Constantinople,ConstantinopleFix"); NETWORKS_TO_RUN = Arrays.asList(networks.split(",")); } @@ -64,7 +64,8 @@ public class BlockchainReferenceTestTools { params.blacklist("RevertPrecompiledTouch_d0g0v0_(EIP158|Byzantium)"); // Consumes a huge amount of memory - params.blacklist("static_Call1MB1024Calldepth_d1g0v0_(Byzantium|Constantinople)"); + params.blacklist( + "static_Call1MB1024Calldepth_d1g0v0_(Byzantium|Constantinople|ConstantinopleFix)"); } public static Collection generateTestParametersForConfig(final String[] filePath) { diff --git a/ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/vm/GeneralStateReferenceTestTools.java b/ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/vm/GeneralStateReferenceTestTools.java index b54d8c03ba..b9d4041e44 100644 --- a/ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/vm/GeneralStateReferenceTestTools.java +++ b/ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/vm/GeneralStateReferenceTestTools.java @@ -51,7 +51,7 @@ public class GeneralStateReferenceTestTools { final String eips = System.getProperty( "test.ethereum.state.eips", - "Frontier,Homestead,EIP150,EIP158,Byzantium,Constantinople"); + "Frontier,Homestead,EIP150,EIP158,Byzantium,Constantinople,ConstantinopleFix"); EIPS_TO_RUN = Arrays.asList(eips.split(",")); } @@ -84,7 +84,7 @@ public class GeneralStateReferenceTestTools { // Gas integer value is too large to construct a valid transaction. params.blacklist("OverflowGasRequire"); // Consumes a huge amount of memory - params.blacklist("static_Call1MB1024Calldepth-(Byzantium|Constantinople)"); + params.blacklist("static_Call1MB1024Calldepth-(Byzantium|Constantinople|ConstantinopleFix)"); } public static Collection generateTestParametersForConfig(final String[] filePath) { diff --git a/ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/vm/ReferenceTestProtocolSchedules.java b/ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/vm/ReferenceTestProtocolSchedules.java index 45313978f1..185ed3924a 100644 --- a/ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/vm/ReferenceTestProtocolSchedules.java +++ b/ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/vm/ReferenceTestProtocolSchedules.java @@ -47,6 +47,9 @@ public class ReferenceTestProtocolSchedules { builder.put("Byzantium", createSchedule(new StubGenesisConfigOptions().byzantiumBlock(0))); builder.put( "Constantinople", createSchedule(new StubGenesisConfigOptions().constantinopleBlock(0))); + builder.put( + "ConstantinopleFix", + createSchedule(new StubGenesisConfigOptions().constantinopleFixBlock(0))); return new ReferenceTestProtocolSchedules(builder.build()); }