diff --git a/config/src/main/java/org/hyperledger/besu/config/GenesisConfigOptions.java b/config/src/main/java/org/hyperledger/besu/config/GenesisConfigOptions.java index 9c0f629948f..00c61fbe62a 100644 --- a/config/src/main/java/org/hyperledger/besu/config/GenesisConfigOptions.java +++ b/config/src/main/java/org/hyperledger/besu/config/GenesisConfigOptions.java @@ -64,6 +64,31 @@ public interface GenesisConfigOptions { */ OptionalLong getClassicForkBlock(); + /** + * Block number for ECIP-1015 fork on Classic network ECIP-1015: Long-term gas cost changes for + * IO-heavy operations to mitigate transaction spam attacks In reference to EIP-150 (ETH Tangerine + * Whistle) Note, this fork happens after Homestead (Mainnet definition) and before DieHard fork + * + * @see https://ecips.ethereumclassic.org/ECIPs/ecip-1015 + * @return block number to activate ECIP-1015 code + */ + OptionalLong getEcip1015BlockNumber(); + + /** + * Block number for DieHard fork on Classic network The DieHard fork includes changes to meet + * specification for ECIP-1010 and EIP-160 Note, this fork happens after ECIP-1015 (classic + * tangerine whistle) and before Gotham fork ECIP-1010: Delay Difficulty Bomb Explosion + * + * @see https://ecips.ethereumclassic.org/ECIPs/ecip-1010 + * EIP-160: EXP cost increase + * @see https://eips.ethereum.org/EIPS/eip-160 + * @return block number to activate Classic DieHard fork + */ + OptionalLong getDieHardBlockNumber(); + Optional getChainId(); OptionalInt getContractSizeLimit(); diff --git a/config/src/main/java/org/hyperledger/besu/config/JsonGenesisConfigOptions.java b/config/src/main/java/org/hyperledger/besu/config/JsonGenesisConfigOptions.java index ee2456b80d7..dd8c2098114 100644 --- a/config/src/main/java/org/hyperledger/besu/config/JsonGenesisConfigOptions.java +++ b/config/src/main/java/org/hyperledger/besu/config/JsonGenesisConfigOptions.java @@ -200,6 +200,16 @@ public OptionalLong getClassicForkBlock() { return getOptionalLong("classicforkblock"); } + @Override + public OptionalLong getEcip1015BlockNumber() { + return getOptionalLong("ecip1015block"); + } + + @Override + public OptionalLong getDieHardBlockNumber() { + return getOptionalLong("diehardblock"); + } + @Override public Optional getChainId() { return getOptionalBigInteger("chainid"); diff --git a/config/src/main/resources/classic.json b/config/src/main/resources/classic.json index 79d17c002ff..a36dfaeaa70 100644 --- a/config/src/main/resources/classic.json +++ b/config/src/main/resources/classic.json @@ -3,6 +3,8 @@ "chainId": 61, "homesteadBlock": 1150000, "classicForkBlock": 1920000, + "ecip1015Block": 2500000, + "diehardBlock": 3000000, "ethash": { } diff --git a/config/src/test-support/java/org/hyperledger/besu/config/StubGenesisConfigOptions.java b/config/src/test-support/java/org/hyperledger/besu/config/StubGenesisConfigOptions.java index 528d0fa597f..052cb53f300 100644 --- a/config/src/test-support/java/org/hyperledger/besu/config/StubGenesisConfigOptions.java +++ b/config/src/test-support/java/org/hyperledger/besu/config/StubGenesisConfigOptions.java @@ -33,6 +33,8 @@ public class StubGenesisConfigOptions implements GenesisConfigOptions { private OptionalLong constantinopleFixBlockNumber = OptionalLong.empty(); private OptionalLong istanbulBlockNumber = OptionalLong.empty(); private OptionalLong classicForkBlock = OptionalLong.empty(); + private OptionalLong ecip1015BlockNumber = OptionalLong.empty(); + private OptionalLong diehardBlockNumber = OptionalLong.empty(); private Optional chainId = Optional.empty(); private OptionalInt contractSizeLimit = OptionalInt.empty(); private OptionalInt stackSizeLimit = OptionalInt.empty(); @@ -127,6 +129,16 @@ public OptionalLong getClassicForkBlock() { return classicForkBlock; } + @Override + public OptionalLong getEcip1015BlockNumber() { + return ecip1015BlockNumber; + } + + @Override + public OptionalLong getDieHardBlockNumber() { + return diehardBlockNumber; + } + @Override public OptionalInt getContractSizeLimit() { return contractSizeLimit; diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ClassicDifficultyCalculators.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ClassicDifficultyCalculators.java new file mode 100644 index 00000000000..f0ce73d83f2 --- /dev/null +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ClassicDifficultyCalculators.java @@ -0,0 +1,54 @@ +/* + * Copyright 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. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.ethereum.mainnet; + +import org.hyperledger.besu.util.uint.UInt256; + +import java.math.BigInteger; + +import com.google.common.primitives.Ints; + +public abstract class ClassicDifficultyCalculators { + private static final BigInteger MINIMUM_DIFFICULTY = BigInteger.valueOf(131_072L); + private static final BigInteger DIFFICULTY_BOUND_DIVISOR = BigInteger.valueOf(2_048L); + private static final BigInteger BIGINT_2 = BigInteger.valueOf(2L); + private static final long EXPONENTIAL_DIFF_PERIOD = 100_000L; + private static final long PAUSE_BLOCK = 3_000_000L; + private static final long FIXED_DIFF = PAUSE_BLOCK / EXPONENTIAL_DIFF_PERIOD; + + public static DifficultyCalculator DIFFICULTY_BOMB_PAUSED = + (time, parent, protocolContext) -> { + final BigInteger parentDifficulty = difficulty(parent.getDifficulty()); + final BigInteger difficulty = + ensureMinimumDifficulty( + BigInteger.valueOf(Math.max(1 - (time - parent.getTimestamp()) / 10, -99L)) + .multiply(parentDifficulty.divide(DIFFICULTY_BOUND_DIVISOR)) + .add(parentDifficulty)); + return adjustForDifficultyPause(FIXED_DIFF, difficulty); + }; + + private static BigInteger adjustForDifficultyPause( + final long periodCount, final BigInteger difficulty) { + return difficulty.add(BIGINT_2.pow(Ints.checkedCast(periodCount - 2))); + } + + private static BigInteger ensureMinimumDifficulty(final BigInteger difficulty) { + return difficulty.compareTo(MINIMUM_DIFFICULTY) < 0 ? MINIMUM_DIFFICULTY : difficulty; + } + + private static BigInteger difficulty(final UInt256 value) { + return new BigInteger(1, value.getBytes().extractArray()); + } +} diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ClassicProtocolSpecs.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ClassicProtocolSpecs.java new file mode 100644 index 00000000000..a87827c1231 --- /dev/null +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ClassicProtocolSpecs.java @@ -0,0 +1,50 @@ +/* + * Copyright 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. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.ethereum.mainnet; + +import java.math.BigInteger; +import java.util.Optional; +import java.util.OptionalInt; + +public class ClassicProtocolSpecs { + + public static ProtocolSpecBuilder classicRecoveryInitDefinition( + final OptionalInt contractSizeLimit, final OptionalInt configStackSizeLimit) { + return MainnetProtocolSpecs.homesteadDefinition(contractSizeLimit, configStackSizeLimit) + .blockHeaderValidatorBuilder(MainnetBlockHeaderValidator::createClassicValidator) + .name("ClassicRecoveryInit"); + } + + public static ProtocolSpecBuilder tangerineWhistleDefinition( + final Optional chainId, + final OptionalInt contractSizeLimit, + final OptionalInt configStackSizeLimit) { + return MainnetProtocolSpecs.homesteadDefinition(contractSizeLimit, configStackSizeLimit) + .gasCalculator(TangerineWhistleGasCalculator::new) + .transactionValidatorBuilder( + gasCalculator -> new MainnetTransactionValidator(gasCalculator, true, chainId)) + .name("ClassicTangerineWhistle"); + } + + public static ProtocolSpecBuilder dieHardDefinition( + final Optional chainId, + final OptionalInt configContractSizeLimit, + final OptionalInt configStackSizeLimit) { + return tangerineWhistleDefinition(chainId, OptionalInt.empty(), configStackSizeLimit) + .gasCalculator(DieHardGasCalculator::new) + .difficultyCalculator(ClassicDifficultyCalculators.DIFFICULTY_BOMB_PAUSED) + .name("DieHard"); + } +} diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/DieHardGasCalculator.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/DieHardGasCalculator.java new file mode 100644 index 00000000000..76e5a977d5f --- /dev/null +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/DieHardGasCalculator.java @@ -0,0 +1,26 @@ +/* + * Copyright 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. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.ethereum.mainnet; + +import org.hyperledger.besu.ethereum.core.Gas; + +public class DieHardGasCalculator extends TangerineWhistleGasCalculator { + private static final Gas EXP_OPERATION_BYTE_GAS_COST = Gas.of(50L); + + @Override + protected Gas expOperationByteGasCost() { + return EXP_OPERATION_BYTE_GAS_COST; + } +} diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ProtocolScheduleBuilder.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ProtocolScheduleBuilder.java index 145f337ea22..c27bedb5659 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ProtocolScheduleBuilder.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ProtocolScheduleBuilder.java @@ -152,6 +152,32 @@ public ProtocolSchedule createProtocolSchedule() { config.getEvmStackSize(), isRevertReasonEnabled)); + // specs for classic network + config + .getClassicForkBlock() + .ifPresent( + classicBlockNumber -> { + final ProtocolSpec originalProtocolSpce = + protocolSchedule.getByBlockNumber(classicBlockNumber); + addProtocolSpec( + protocolSchedule, + OptionalLong.of(classicBlockNumber), + ClassicProtocolSpecs.classicRecoveryInitDefinition( + config.getContractSizeLimit(), config.getEvmStackSize())); + protocolSchedule.putMilestone(classicBlockNumber + 10, originalProtocolSpce); + }); + + addProtocolSpec( + protocolSchedule, + config.getEcip1015BlockNumber(), + ClassicProtocolSpecs.tangerineWhistleDefinition( + chainId, config.getContractSizeLimit(), config.getEvmStackSize())); + addProtocolSpec( + protocolSchedule, + config.getDieHardBlockNumber(), + ClassicProtocolSpecs.dieHardDefinition( + chainId, config.getContractSizeLimit(), config.getEvmStackSize())); + LOG.info("Protocol schedule created with milestones: {}", protocolSchedule.listMilestones()); return protocolSchedule; }