import times, eth/common, stint, ../constants, ../chain_config const ExpDiffPeriod = 100000.u256 DifficultyBoundDivisorU = 2048.u256 DifficultyBoundDivisorI = 2048.i256 DurationLimit = 13 MinimumDifficultyU = 131072.u256 MinimumDifficultyI = 131072.i256 bigOne = 1.u256 bigTwo = 2.u256 bigNine = 9.i256 bigOneI = 1.i256 bigTwoI = 2.i256 bigTenI = 10.i256 bigMin99 = -99.i256 template difficultyBomb(periodCount: UInt256) = periodCount = periodCount div ExpDiffPeriod if periodCount > bigOne: # diff = diff + 2^(periodCount - 2) var expDiff = periodCount - bigTwo expDiff = bigTwo.pow(expDiff) diff = diff + expDiff diff = max(diff, MinimumDifficultyU) # calcDifficultyFrontier is the difficulty adjustment algorithm. It returns the # difficulty that a new block should have when created at time given the parent # block's time and difficulty. The calculation uses the Frontier rules. func calcDifficultyFrontier*(timeStamp: EthTime, parent: BlockHeader): DifficultyInt = var diff: DifficultyInt let adjust = parent.difficulty div DifficultyBoundDivisorU let time = timeStamp.toUnix() let parentTime = parent.timestamp.toUnix() if time - parentTime < DurationLimit: diff = parent.difficulty + adjust else: diff = parent.difficulty - adjust diff = max(diff, MinimumDifficultyU) var periodCount = parent.blockNumber + bigOne difficultyBomb(periodCount) result = diff # calcDifficultyHomestead is the difficulty adjustment algorithm. It returns # the difficulty that a new block should have when created at time given the # parent block's time and difficulty. The calculation uses the Homestead rules. func calcDifficultyHomestead*(timeStamp: EthTime, parent: BlockHeader): DifficultyInt = # https:#github.com/ethereum/EIPs/blob/master/EIPS/eip-2.md # algorithm: # diff = (parent_diff + # (parent_diff / 2048 * max(1 - (block_timestamp - parent_timestamp) # 10, -99)) # ) + 2^(periodCount - 2) let time = timeStamp.toUnix() let parentTime = parent.timestamp.toUnix() let parentDifficulty = cast[Int256](parent.difficulty) # 1 - (block_timestamp - parent_timestamp) # 10 var x = (time - parentTime).i256 x = x div bigTenI x = bigOneI - x # max(1 - (block_timestamp - parent_timestamp) # 10, -99) x = max(x, bigMin99) # (parent_diff + parent_diff # 2048 * max(1 - (block_timestamp - parent_timestamp) # 10, -99)) var y = parentDifficulty div DifficultyBoundDivisorI x = y * x x = parentDifficulty + x # minimum difficulty can ever be (before exponential factor) var diff = cast[UInt256](max(x, MinimumDifficultyI)) # for the exponential factor var periodCount = parent.blockNumber + bigOne difficultyBomb(periodCount) result = diff # makeDifficultyCalculator creates a difficultyCalculator with the given bomb-delay. # the difficulty is calculated with Byzantium rules, which differs from Homestead in # how uncles affect the calculation func makeDifficultyCalculator(bombDelay: static[int], timeStamp: EthTime, parent: BlockHeader): DifficultyInt = # Note, the calculations below looks at the parent number, which is 1 below # the block number. Thus we remove one from the delay given const bombDelayFromParent = bombDelay.u256 - bigOne # https:#github.com/ethereum/EIPs/issues/100. # algorithm: # diff = (parent_diff + # (parent_diff / 2048 * max((2 if len(parent.uncles) else 1) - ((timestamp - parent.timestamp) # 9), -99)) # ) + 2^(periodCount - 2) let time = timeStamp.toUnix() let parentTime = parent.timestamp.toUnix() let parentDifficulty = cast[Int256](parent.difficulty) # (2 if len(parent_uncles) else 1) - (block_timestamp - parent_timestamp) # 9 var x = (time - parentTime).i256 x = x div bigNine if parent.ommersHash == EMPTY_UNCLE_HASH: x = bigOneI - x else: x = bigTwoI - x # max((2 if len(parent_uncles) else 1) - (block_timestamp - parent_timestamp) # 9, -99) x = max(x, bigMin99) # parent_diff + (parent_diff / 2048 * max((2 if len(parent.uncles) else 1) - ((timestamp - parent.timestamp) # 9), -99)) var y = parentDifficulty div DifficultyBoundDivisorI x = y * x x = parentDifficulty + x # minimum difficulty can ever be (before exponential factor) var diff = cast[UInt256](max(x, MinimumDifficultyI)) # calculate a fake block number for the ice-age delay # Specification: https:#eips.ethereum.org/EIPS/eip-1234 var periodCount: UInt256 if parent.blockNumber >= bombDelayFromParent: periodCount = parent.blockNumber - bombDelayFromParent difficultyBomb(periodCount) result = diff template calcDifficultyByzantium*(timeStamp: EthTime, parent: BlockHeader): DifficultyInt = ## "EIP-649: Metropolis Difficulty Bomb Delay and Block Reward Reduction" ## makeDifficultyCalculator(3_000_000, timeStamp, parent) template calcDifficultyConstantinople*(timeStamp: EthTime, parent: BlockHeader): DifficultyInt = ## "EIP-1234: Constantinople Difficulty Bomb Delay and Block Reward Adjustment" ## ## Keep using Byzantium's rules but offset the bomb 5.0M blocks. makeDifficultyCalculator(5_000_000, timeStamp, parent) template calcDifficultyMuirGlacier*(timeStamp: EthTime, parent: BlockHeader): DifficultyInt = ## "EIP-2384: Muir Glacier Difficulty Bomb Delay" ## ## Offset the bomb 4.0M more blocks than Constantinople, total 9.0M blocks. makeDifficultyCalculator(9_000_000, timeStamp, parent) template calcDifficultyLondon*(timeStamp: EthTime, parent: BlockHeader): DifficultyInt = ## "EIP-3554: Difficulty Bomb Delay to December 2021" ## ## Offset the bomb a total of 9.7M blocks. makeDifficultyCalculator(9_700_000, timeStamp, parent) template calcDifficultyArrowGlacier*(timeStamp: EthTime, parent: BlockHeader): DifficultyInt = ## "EIP-4345: Difficulty Bomb Delay to June 2022" ## ## Offset the bomb a total of 10.7M blocks. makeDifficultyCalculator(10_700_000, timeStamp, parent) template calcDifficultyGrayGlacier*(timeStamp: EthTime, parent: BlockHeader): DifficultyInt = ## "EIP-4345: Difficulty Bomb Delay to September 2022" ## ## Offset the bomb a total of 11.4M blocks. makeDifficultyCalculator(11_400_000, timeStamp, parent) func calcDifficulty*(c: ChainConfig, timeStamp: EthTime, parent: BlockHeader): DifficultyInt = let next = parent.blockNumber + bigOne if next >= c.grayGlacierBlock: result = calcDifficultyGrayGlacier(timeStamp, parent) elif next >= c.arrowGlacierBlock: result = calcDifficultyArrowGlacier(timeStamp, parent) elif next >= c.londonBlock: result = calcDifficultyLondon(timeStamp, parent) elif next >= c.muirGlacierBlock: result = calcDifficultyMuirGlacier(timeStamp, parent) elif next >= c.constantinopleBlock: result = calcDifficultyConstantinople(timeStamp, parent) elif next >= c.byzantiumBlock: result = calcDifficultyByzantium(timeStamp, parent) elif next >= c.homesteadBlock: result = calcDifficultyHomestead(timeStamp, parent) else: result = calcDifficultyFrontier(timeStamp, parent)