From b20ae801509e63590126b0c96efb79d3e7eb9c73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20=C5=9Al=C4=85ski?= Date: Wed, 4 Nov 2020 14:20:19 +0100 Subject: [PATCH] etcm-193 added possibility to configure target PoW time --- .../EthashTestBlockHeaderValidator.scala | 3 +- src/main/resources/chains/etc-chain.conf | 4 +++ src/main/resources/chains/eth-chain.conf | 4 +++ src/main/resources/chains/mordor-chain.conf | 4 +++ src/main/resources/chains/ropsten-chain.conf | 4 +++ src/main/resources/chains/test-chain.conf | 4 +++ .../chains/testnet-internal-chain.conf | 4 +++ .../difficulty/ConstantDifficulty.scala | 7 ---- .../difficulty/DifficultyCalculator.scala | 15 ++++++++ .../consensus/ethash/EthashConsensus.scala | 10 +++--- .../EthashDifficultyCalculator.scala | 10 +++--- .../TargetTimeDifficultyCalculator.scala | 34 +++++++++++++++++++ .../EthashBlockHeaderValidator.scala | 3 +- .../MockedPowBlockHeaderValidator.scala | 3 +- .../ethereum/testmode/TestmodeConsensus.scala | 3 +- .../ethereum/utils/BlockchainConfig.scala | 8 +++-- src/test/resources/application.conf | 1 + 17 files changed, 93 insertions(+), 28 deletions(-) delete mode 100644 src/main/scala/io/iohk/ethereum/consensus/difficulty/ConstantDifficulty.scala create mode 100644 src/main/scala/io/iohk/ethereum/consensus/ethash/difficulty/TargetTimeDifficultyCalculator.scala diff --git a/src/ets/scala/io/iohk/ethereum/ets/blockchain/EthashTestBlockHeaderValidator.scala b/src/ets/scala/io/iohk/ethereum/ets/blockchain/EthashTestBlockHeaderValidator.scala index c10cde0289..9c7d8eab62 100644 --- a/src/ets/scala/io/iohk/ethereum/ets/blockchain/EthashTestBlockHeaderValidator.scala +++ b/src/ets/scala/io/iohk/ethereum/ets/blockchain/EthashTestBlockHeaderValidator.scala @@ -1,7 +1,6 @@ package io.iohk.ethereum.ets.blockchain import io.iohk.ethereum.consensus.difficulty.DifficultyCalculator -import io.iohk.ethereum.consensus.ethash.difficulty.EthashDifficultyCalculator import io.iohk.ethereum.consensus.ethash.validators.EthashBlockHeaderValidator import io.iohk.ethereum.consensus.validators.{ BlockHeaderError, BlockHeaderValid, BlockHeaderValidatorSkeleton } import io.iohk.ethereum.domain.BlockHeader @@ -14,7 +13,7 @@ class EthashTestBlockHeaderValidator(blockchainConfig: BlockchainConfig) extends // we need concurrent map since validators can be used from multiple places protected val powCaches: java.util.concurrent.ConcurrentMap[Long, PowCacheData] = new java.util.concurrent.ConcurrentHashMap[Long, PowCacheData]() - protected def difficulty: DifficultyCalculator = new EthashDifficultyCalculator(blockchainConfig) + protected def difficulty: DifficultyCalculator = DifficultyCalculator(blockchainConfig) def validateEvenMore(blockHeader: BlockHeader, parentHeader: BlockHeader): Either[BlockHeaderError, BlockHeaderValid] = Right(BlockHeaderValid) diff --git a/src/main/resources/chains/etc-chain.conf b/src/main/resources/chains/etc-chain.conf index 43abf1a636..4b7dbb8f0f 100644 --- a/src/main/resources/chains/etc-chain.conf +++ b/src/main/resources/chains/etc-chain.conf @@ -3,6 +3,10 @@ # 1 - mainnet, 3 - ropsten, 7 - mordor network-id = 1 + # Possibility to set Proof of Work target time for testing purposes. + # null means that the standard difficulty calculation rules are used + pow-target-time = null + # Frontier block number frontier-block-number = "0" diff --git a/src/main/resources/chains/eth-chain.conf b/src/main/resources/chains/eth-chain.conf index 15230e8c42..b7e0e3aee0 100644 --- a/src/main/resources/chains/eth-chain.conf +++ b/src/main/resources/chains/eth-chain.conf @@ -3,6 +3,10 @@ # 1 - mainnet, 3 - ropsten, 7 - mordor network-id = 1 + # Possibility to set Proof of Work target time for testing purposes. + # null means that the standard difficulty calculation rules are used + pow-target-time = null + # Frontier block number frontier-block-number = "0" diff --git a/src/main/resources/chains/mordor-chain.conf b/src/main/resources/chains/mordor-chain.conf index ad6154120d..f061a2ea03 100644 --- a/src/main/resources/chains/mordor-chain.conf +++ b/src/main/resources/chains/mordor-chain.conf @@ -3,6 +3,10 @@ # 1 - mainnet, 3 - ropsten, 7 - mordor network-id = 7 + # Possibility to set Proof of Work target time for testing purposes. + # null means that the standard difficulty calculation rules are used + pow-target-time = null + # Frontier block number frontier-block-number = "0" diff --git a/src/main/resources/chains/ropsten-chain.conf b/src/main/resources/chains/ropsten-chain.conf index 4c21e99118..2db955782a 100644 --- a/src/main/resources/chains/ropsten-chain.conf +++ b/src/main/resources/chains/ropsten-chain.conf @@ -3,6 +3,10 @@ # 1 - mainnet, 3 - ropsten, 7 - mordor network-id = 3 + # Possibility to set Proof of Work target time for testing purposes. + # null means that the standard difficulty calculation rules are used + pow-target-time = null + # Frontier block number frontier-block-number = "0" diff --git a/src/main/resources/chains/test-chain.conf b/src/main/resources/chains/test-chain.conf index 3f00f5a69e..e9ddab7ffd 100644 --- a/src/main/resources/chains/test-chain.conf +++ b/src/main/resources/chains/test-chain.conf @@ -3,6 +3,10 @@ # 1 - mainnet, 7 - mordor network-id = 1 + # Possibility to set Proof of Work target time for testing purposes. + # null means that the standard difficulty calculation rules are used + pow-target-time = null + # Frontier block number frontier-block-number = "0" diff --git a/src/main/resources/chains/testnet-internal-chain.conf b/src/main/resources/chains/testnet-internal-chain.conf index 56ba51d92e..08e65563a2 100644 --- a/src/main/resources/chains/testnet-internal-chain.conf +++ b/src/main/resources/chains/testnet-internal-chain.conf @@ -3,6 +3,10 @@ # 1 - mainnet, 3 - ropsten, 7 - mordor network-id = 42 + # Possibility to set Proof of Work target time for testing purposes. + # null means that the standard difficulty calculation rules are used + pow-target-time = null + # Frontier block number frontier-block-number = "0" diff --git a/src/main/scala/io/iohk/ethereum/consensus/difficulty/ConstantDifficulty.scala b/src/main/scala/io/iohk/ethereum/consensus/difficulty/ConstantDifficulty.scala deleted file mode 100644 index 63b86fd7cb..0000000000 --- a/src/main/scala/io/iohk/ethereum/consensus/difficulty/ConstantDifficulty.scala +++ /dev/null @@ -1,7 +0,0 @@ -package io.iohk.ethereum.consensus.difficulty - -import io.iohk.ethereum.domain.BlockHeader - -class ConstantDifficulty(c: BigInt) extends DifficultyCalculator { - final def calculateDifficulty(blockNumber: BigInt, blockTimestamp: Long, parent: BlockHeader): BigInt = c -} diff --git a/src/main/scala/io/iohk/ethereum/consensus/difficulty/DifficultyCalculator.scala b/src/main/scala/io/iohk/ethereum/consensus/difficulty/DifficultyCalculator.scala index 12a3969d83..29552c7e5b 100644 --- a/src/main/scala/io/iohk/ethereum/consensus/difficulty/DifficultyCalculator.scala +++ b/src/main/scala/io/iohk/ethereum/consensus/difficulty/DifficultyCalculator.scala @@ -1,7 +1,22 @@ package io.iohk.ethereum.consensus.difficulty +import io.iohk.ethereum.consensus.ethash.difficulty.{TargetTimeDifficultyCalculator, EthashDifficultyCalculator} import io.iohk.ethereum.domain.BlockHeader +import io.iohk.ethereum.utils.BlockchainConfig trait DifficultyCalculator { def calculateDifficulty(blockNumber: BigInt, blockTimestamp: Long, parent: BlockHeader): BigInt } + +object DifficultyCalculator { + def apply(blockchainConfig: BlockchainConfig): DifficultyCalculator = { + blockchainConfig.powTargetTime match { + case Some(targetTime) => new TargetTimeDifficultyCalculator(targetTime) + case None => new EthashDifficultyCalculator(blockchainConfig) + } + } + + val DifficultyBoundDivision: Int = 2048 + val FrontierTimestampDiffLimit: Int = -99 + val MinimumDifficulty: BigInt = 131072 +} diff --git a/src/main/scala/io/iohk/ethereum/consensus/ethash/EthashConsensus.scala b/src/main/scala/io/iohk/ethereum/consensus/ethash/EthashConsensus.scala index 1a0e08aacf..00e2fe2de4 100644 --- a/src/main/scala/io/iohk/ethereum/consensus/ethash/EthashConsensus.scala +++ b/src/main/scala/io/iohk/ethereum/consensus/ethash/EthashConsensus.scala @@ -2,10 +2,13 @@ package io.iohk.ethereum package consensus package ethash +import java.util.concurrent.atomic.AtomicReference + import akka.actor.ActorRef import akka.util.Timeout import io.iohk.ethereum.consensus.Protocol.{Ethash, MockedPow} import io.iohk.ethereum.consensus.blocks.TestBlockGenerator +import io.iohk.ethereum.consensus.difficulty.DifficultyCalculator import io.iohk.ethereum.consensus.ethash.MinerResponses.MinerNotExist import io.iohk.ethereum.consensus.ethash.blocks.{EthashBlockGenerator, EthashBlockGeneratorImpl} import io.iohk.ethereum.consensus.ethash.validators.ValidatorsExecutor @@ -15,9 +18,6 @@ import io.iohk.ethereum.ledger.BlockPreparator import io.iohk.ethereum.ledger.Ledger.VMImpl import io.iohk.ethereum.nodebuilder.Node import io.iohk.ethereum.utils.{BlockchainConfig, Logger} -import java.util.concurrent.atomic.AtomicReference - -import io.iohk.ethereum.consensus.ethash.difficulty.EthashDifficultyCalculator import scala.concurrent.Future import scala.concurrent.duration._ @@ -32,7 +32,7 @@ class EthashConsensus private ( val config: FullConsensusConfig[EthashConfig], val validators: ValidatorsExecutor, val blockGenerator: EthashBlockGenerator, - val difficultyCalculator: EthashDifficultyCalculator + val difficultyCalculator: DifficultyCalculator ) extends TestConsensus with Logger { @@ -178,7 +178,7 @@ object EthashConsensus { validators: ValidatorsExecutor ): EthashConsensus = { - val difficultyCalculator = new EthashDifficultyCalculator(blockchainConfig) + val difficultyCalculator = DifficultyCalculator(blockchainConfig) val blockPreparator = new BlockPreparator( vm = vm, diff --git a/src/main/scala/io/iohk/ethereum/consensus/ethash/difficulty/EthashDifficultyCalculator.scala b/src/main/scala/io/iohk/ethereum/consensus/ethash/difficulty/EthashDifficultyCalculator.scala index f2abc35942..6738ece5f3 100644 --- a/src/main/scala/io/iohk/ethereum/consensus/ethash/difficulty/EthashDifficultyCalculator.scala +++ b/src/main/scala/io/iohk/ethereum/consensus/ethash/difficulty/EthashDifficultyCalculator.scala @@ -6,13 +6,11 @@ import io.iohk.ethereum.utils.BlockchainConfig class EthashDifficultyCalculator(blockchainConfig: BlockchainConfig) extends DifficultyCalculator { import blockchainConfig._ + import DifficultyCalculator._ - val DifficultyBoundDivision: Int = 2048 - val FrontierTimestampDiffLimit: Int = -99 - val ExpDifficultyPeriod: Int = 100000 - val MinimumDifficulty: BigInt = 131072 - val ByzantiumRelaxDifficulty: BigInt = 3000000 - val ConstantinopleRelaxDifficulty: BigInt = 5000000 + private val ExpDifficultyPeriod: Int = 100000 + private val ByzantiumRelaxDifficulty: BigInt = 3000000 + private val ConstantinopleRelaxDifficulty: BigInt = 5000000 def calculateDifficulty(blockNumber: BigInt, blockTimestamp: Long, parentHeader: BlockHeader): BigInt = { lazy val timestampDiff = blockTimestamp - parentHeader.unixTimestamp diff --git a/src/main/scala/io/iohk/ethereum/consensus/ethash/difficulty/TargetTimeDifficultyCalculator.scala b/src/main/scala/io/iohk/ethereum/consensus/ethash/difficulty/TargetTimeDifficultyCalculator.scala new file mode 100644 index 0000000000..325b36c7de --- /dev/null +++ b/src/main/scala/io/iohk/ethereum/consensus/ethash/difficulty/TargetTimeDifficultyCalculator.scala @@ -0,0 +1,34 @@ +package io.iohk.ethereum.consensus.ethash.difficulty + +import io.iohk.ethereum.consensus.difficulty.DifficultyCalculator +import io.iohk.ethereum.domain.BlockHeader + +class TargetTimeDifficultyCalculator(powTargetTime: Long) extends DifficultyCalculator { + + import DifficultyCalculator._ + + /** + * The lowerBoundExpectedRatio (l for abbreviation below) divides the timestamp diff into ranges: + * [0, l) => c = 1, difficulty increases + * [l, 2*l) => c = 0. difficulty stays the same + * ... + * [l*i, l*(i+1) ) => c = 1-i, difficulty decreases + * + * example: + * powTargetTime := 45 seconds + * l := 30 seconds + * [0, 0.5 min) => difficulty increases + * [0.5 min, 1 min) => difficulty stays the same (the average should be powTargetTime) + * [1 min, +infinity) => difficulty decreases + */ + private val lowerBoundExpectedRatio: Long = (powTargetTime / 1.5).toLong + + def calculateDifficulty(blockNumber: BigInt, blockTimestamp: Long, parentHeader: BlockHeader): BigInt = { + val timestampDiff = blockTimestamp - parentHeader.unixTimestamp + + val x: BigInt = parentHeader.difficulty / DifficultyBoundDivision + val c: BigInt = math.max(1 - (timestampDiff / lowerBoundExpectedRatio), FrontierTimestampDiffLimit) + + MinimumDifficulty.max(parentHeader.difficulty + x * c) + } +} diff --git a/src/main/scala/io/iohk/ethereum/consensus/ethash/validators/EthashBlockHeaderValidator.scala b/src/main/scala/io/iohk/ethereum/consensus/ethash/validators/EthashBlockHeaderValidator.scala index 25c3ccbaa7..c7793e8388 100644 --- a/src/main/scala/io/iohk/ethereum/consensus/ethash/validators/EthashBlockHeaderValidator.scala +++ b/src/main/scala/io/iohk/ethereum/consensus/ethash/validators/EthashBlockHeaderValidator.scala @@ -3,7 +3,6 @@ package validators import akka.util.ByteString import io.iohk.ethereum.consensus.difficulty.DifficultyCalculator -import io.iohk.ethereum.consensus.ethash.difficulty.EthashDifficultyCalculator import io.iohk.ethereum.consensus.validators.BlockHeaderError.HeaderPoWError import io.iohk.ethereum.consensus.validators.{BlockHeaderError, BlockHeaderValid, BlockHeaderValidatorSkeleton} import io.iohk.ethereum.crypto @@ -22,7 +21,7 @@ class EthashBlockHeaderValidator(blockchainConfig: BlockchainConfig) // we need atomic since validators can be used from multiple places protected val powCaches: AtomicAny[List[PowCacheData]] = Atomic(List.empty[PowCacheData]) - protected def difficulty: DifficultyCalculator = new EthashDifficultyCalculator(blockchainConfig) + protected def difficulty: DifficultyCalculator = DifficultyCalculator(blockchainConfig) def validateEvenMore( blockHeader: BlockHeader, diff --git a/src/main/scala/io/iohk/ethereum/consensus/ethash/validators/MockedPowBlockHeaderValidator.scala b/src/main/scala/io/iohk/ethereum/consensus/ethash/validators/MockedPowBlockHeaderValidator.scala index b0d12414e9..a88daacaee 100644 --- a/src/main/scala/io/iohk/ethereum/consensus/ethash/validators/MockedPowBlockHeaderValidator.scala +++ b/src/main/scala/io/iohk/ethereum/consensus/ethash/validators/MockedPowBlockHeaderValidator.scala @@ -2,7 +2,6 @@ package io.iohk.ethereum.consensus.ethash package validators import io.iohk.ethereum.consensus.difficulty.DifficultyCalculator -import io.iohk.ethereum.consensus.ethash.difficulty.EthashDifficultyCalculator import io.iohk.ethereum.consensus.validators.{BlockHeaderError, BlockHeaderValid, BlockHeaderValidatorSkeleton} import io.iohk.ethereum.domain.BlockHeader import io.iohk.ethereum.utils.BlockchainConfig @@ -10,7 +9,7 @@ import io.iohk.ethereum.utils.BlockchainConfig class MockedPowBlockHeaderValidator(blockchainConfig: BlockchainConfig) extends BlockHeaderValidatorSkeleton(blockchainConfig) { - protected def difficulty: DifficultyCalculator = new EthashDifficultyCalculator(blockchainConfig) + protected def difficulty: DifficultyCalculator = DifficultyCalculator(blockchainConfig) def validateEvenMore( blockHeader: BlockHeader, diff --git a/src/main/scala/io/iohk/ethereum/testmode/TestmodeConsensus.scala b/src/main/scala/io/iohk/ethereum/testmode/TestmodeConsensus.scala index e22a82baff..8273f65385 100644 --- a/src/main/scala/io/iohk/ethereum/testmode/TestmodeConsensus.scala +++ b/src/main/scala/io/iohk/ethereum/testmode/TestmodeConsensus.scala @@ -5,7 +5,6 @@ import io.iohk.ethereum.consensus._ import io.iohk.ethereum.consensus.blocks.{BlockTimestampProvider, NoOmmersBlockGenerator, TestBlockGenerator} import io.iohk.ethereum.consensus.difficulty.DifficultyCalculator import io.iohk.ethereum.consensus.ethash.MinerResponses.MinerNotExist -import io.iohk.ethereum.consensus.ethash.difficulty.EthashDifficultyCalculator import io.iohk.ethereum.consensus.ethash.{MinerProtocol, MinerResponse} import io.iohk.ethereum.consensus.validators._ import io.iohk.ethereum.consensus.validators.std.{StdBlockValidator, StdSignedTransactionValidator} @@ -99,6 +98,6 @@ trait TestmodeConsensusBuilder extends ConsensusBuilder { blockchain, blockchainConfig, consensusConfig, - new EthashDifficultyCalculator(blockchainConfig) + DifficultyCalculator(blockchainConfig) ) } diff --git a/src/main/scala/io/iohk/ethereum/utils/BlockchainConfig.scala b/src/main/scala/io/iohk/ethereum/utils/BlockchainConfig.scala index bb0a0173b3..fbfa8f671b 100644 --- a/src/main/scala/io/iohk/ethereum/utils/BlockchainConfig.scala +++ b/src/main/scala/io/iohk/ethereum/utils/BlockchainConfig.scala @@ -1,15 +1,15 @@ package io.iohk.ethereum.utils import akka.util.ByteString +import com.typesafe.config.{Config => TypesafeConfig} import io.iohk.ethereum.domain.{Address, UInt256} import io.iohk.ethereum.utils.NumericUtils._ import scala.collection.JavaConverters._ -import com.typesafe.config.{Config => TypesafeConfig} - import scala.util.Try case class BlockchainConfig( + powTargetTime: Option[Long] = None, frontierBlockNumber: BigInt, homesteadBlockNumber: BigInt, eip106BlockNumber: BigInt, @@ -50,6 +50,9 @@ object BlockchainConfig { // scalastyle:off method.length def fromRawConfig(blockchainConfig: TypesafeConfig): BlockchainConfig = { + val powTargetTime: Option[Long] = + ConfigUtils + .getOptionalValue(blockchainConfig, "pow-target-time", _.getDuration("pow-target-time").getSeconds) val frontierBlockNumber: BigInt = BigInt(blockchainConfig.getString("frontier-block-number")) val homesteadBlockNumber: BigInt = BigInt(blockchainConfig.getString("homestead-block-number")) val eip106BlockNumber: BigInt = BigInt(blockchainConfig.getString("eip106-block-number")) @@ -105,6 +108,7 @@ object BlockchainConfig { val ecip1099BlockNumber: BigInt = BigInt(blockchainConfig.getString("ecip1099-block-number")) BlockchainConfig( + powTargetTime = powTargetTime, frontierBlockNumber = frontierBlockNumber, homesteadBlockNumber = homesteadBlockNumber, eip106BlockNumber = eip106BlockNumber, diff --git a/src/test/resources/application.conf b/src/test/resources/application.conf index 6635c1398b..d53cc1f1b6 100644 --- a/src/test/resources/application.conf +++ b/src/test/resources/application.conf @@ -22,6 +22,7 @@ mantis { network = "test" test { + pow-target-time = null frontier-block-number = "0" eip106-block-number = "1000000000000000000" eip150-block-number = "2463000"