/// <summary> /// Calculates the difficulty for the next block given a previous blocks header, the timestamp of the new block, and the configuration. /// </summary> /// <param name="parentBlockHeader">The previous block header from which we derive difficulty.</param> /// <param name="timestamp">The timestamp of the new block.</param> /// <param name="configuration">The configuration under which we calculate difficulty.</param> /// <returns>Returns the difficulty for the supposed new block.</returns> public static BigInteger CalculateDifficulty(BlockHeader parentBlockHeader, BigInteger timestamp, Configuration.Configuration configuration) { // Handle the special case of difficulty for test chains (difficulty 1 keeps difficulty at 1). if (parentBlockHeader.Difficulty == 1) { return(1); } // Check our versioning BigInteger newBlockNumber = parentBlockHeader.BlockNumber + 1; bool isByzantium = newBlockNumber >= configuration.GetReleaseStartBlockNumber(Configuration.EthereumRelease.Byzantium); bool isHomestead = newBlockNumber >= configuration.GetReleaseStartBlockNumber(Configuration.EthereumRelease.Homestead); // TODO: Revisit the rest of this. Currently a poor implementation. Use following as source: https://dltlabs.com/how-difficulty-adjustment-algorithm-works-in-ethereum/ BigInteger offset = parentBlockHeader.Difficulty / configuration.DifficultyFactor; BigInteger sign = 0; BigInteger deltaTime = timestamp - parentBlockHeader.Timestamp; if (isByzantium) { BigInteger x = 2; if (parentBlockHeader.UnclesHash.ValuesEqual(BLANK_UNCLES_HASH)) { x = 1; } sign = BigInteger.Max(-99, x - (deltaTime / configuration.DifficultyAdjustmentCutOffByzantium)); } else if (isHomestead) { sign = BigInteger.Max(-99, 1 - (deltaTime / configuration.DifficultyAdjustmentCutOffHomestead)); } else { if (deltaTime < configuration.DifficultyAdjustmentCutOff) { sign = 1; } else { sign = -1; } } BigInteger minDifficulty = BigInteger.Min(configuration.MinDifficulty, parentBlockHeader.Difficulty); BigInteger difficulty = BigInteger.Max(minDifficulty, parentBlockHeader.Difficulty + (offset * sign)); BigInteger periods = newBlockNumber / configuration.DifficultyExponentialPeriod; if (isByzantium) { periods -= configuration.DifficultyExponentialFreePeriodsByzantium; } if (periods >= configuration.DifficultyExponentialFreePeriods) { BigInteger exponent = periods - configuration.DifficultyExponentialFreePeriods; return(BigInteger.Max(difficulty + BigInteger.Pow(2, (int)exponent), configuration.MinDifficulty)); } return(difficulty); }