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 OptionalLong getConstantinopleBlockNumber() {
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 OptionalLong getConstantinopleBlockNumber() {
return constantinopleBlockNumber;
}
+ @Override
+ public OptionalLong getConstantinopleFixBlockNumber() {
+ return constantinopleFixBlockNumber;
+ }
+
@Override
public OptionalInt getChainId() {
return chainId;
@@ -125,6 +131,11 @@ public StubGenesisConfigOptions constantinopleBlock(final long blockNumber) {
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 void shouldGetConstantinopleBlockNumber() {
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 void shouldNotReturnEmptyOptionalWhenBlockNumberNotSpecified() {
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 static ProtocolSpecBuilder constantinopleDefinition(final int chain
.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 ProtocolSchedule createProtocolSchedule() {
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 ProtocolSchedule createProtocolSchedule() {
protocolSchedule,
config.getConstantinopleBlockNumber(),
MainnetProtocolSpecs.constantinopleDefinition(chainId));
+ addProtocolSpec(
+ protocolSchedule,
+ config.getConstantinopleFixBlockNumber(),
+ MainnetProtocolSpecs.constantinopleFixDefinition(chainId));
return protocolSchedule;
}
@@ -101,4 +107,34 @@ private void addProtocolSpec(
.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 ExecutionContextTestFixture build() {
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 Expectation expectation(final String milestone) {
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 void shouldReturnDefaultProtocolSpecsWhenCustomNumbersAreNotUsed() {
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 void shouldOnlyUseFrontierWhenEmptyJsonConfigIsUsed() {
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 void createFromConfigWithSettings() {
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 void shouldCreateRopstenConfig() throws Exception {
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