public uint GetRequiredNextBits(Chain chain)
{
var powLimitCompact = DataCalculator.ToCompact(ChainParams.HighestTarget);
if (chain.Height == 0)
return powLimitCompact;
var prevHeader = chain.Blocks[chain.Height - 1];
if (ChainParams.PowNoRetargeting)
return prevHeader.Bits;
// not on an adjustment interval, use previous block's target
if (chain.Height % ChainParams.DifficultyInterval != 0)
{
if (!ChainParams.AllowMininimumDifficultyBlocks)
return prevHeader.Bits;
else
{
// Special difficulty rule for testnet:
// If the new block's timestamp is more than 2* 10 minutes
// then allow mining of a min-difficulty block.
var currentHeader = chain.LastBlock;
if (currentHeader.Time > prevHeader.Time + TimeSpan.FromTicks(ChainParams.PowTargetSpacing.Ticks * 2))
return powLimitCompact;
else
{
// Return the last non-special-min-difficulty-rules-block
var header = prevHeader;
while (header.Height > 0
&& header.Height % ChainParams.DifficultyInterval != 0
&& header.Bits == powLimitCompact)
{
header = chain.Blocks[header.Height - 1];
}
return header.Bits;
}
}
}
// on an adjustment interval, calculate the required next target
else
{
// get the block difficultyInterval blocks ago
var prevIntervalHeight = prevHeader.Height - (ChainParams.DifficultyInterval - 1);
var prevIntervalHeader = chain.Blocks[prevIntervalHeight];
var actualTimespan = (uint)(prevHeader.Time - prevIntervalHeader.Time).TotalSeconds;
var targetTimespan = (uint)(ChainParams.DifficultyTargetTimespan).TotalSeconds;
// limit adjustment to 4x or 1/4x
if (actualTimespan < targetTimespan / 4)
actualTimespan = targetTimespan / 4;
else if (actualTimespan > targetTimespan * 4)
actualTimespan = targetTimespan * 4;
// calculate the new target
var target = prevHeader.BlockHeader.CalculateTarget();
target *= actualTimespan;
target /= targetTimespan;
// make sure target isn't too high (too low difficulty)
if (target > ChainParams.HighestTarget)
target = ChainParams.HighestTarget;
return DataCalculator.ToCompact(target);
}
}