Add function to validate difficulty changes

The rule against difficulty adjustments changing by more than a factor of 4 can
be helpful for anti-DoS measures in contexts where we lack a full headers
chain, so expose this functionality separately and in the narrow case where we
only know the height, new value, and old value.

Includes fuzz test by Martin Zumsande.
This commit is contained in:
Suhas Daftuar
2022-05-25 10:16:56 -04:00
parent 2bd9aa5a44
commit 1d4cfa4272
4 changed files with 125 additions and 4 deletions

View File

@@ -71,6 +71,57 @@ unsigned int CalculateNextWorkRequired(const CBlockIndex* pindexLast, int64_t nF
return bnNew.GetCompact();
}
// Check that on difficulty adjustments, the new difficulty does not increase
// or decrease beyond the permitted limits.
bool PermittedDifficultyTransition(const Consensus::Params& params, int64_t height, uint32_t old_nbits, uint32_t new_nbits)
{
if (params.fPowAllowMinDifficultyBlocks) return true;
if (height % params.DifficultyAdjustmentInterval() == 0) {
int64_t smallest_timespan = params.nPowTargetTimespan/4;
int64_t largest_timespan = params.nPowTargetTimespan*4;
const arith_uint256 pow_limit = UintToArith256(params.powLimit);
arith_uint256 observed_new_target;
observed_new_target.SetCompact(new_nbits);
// Calculate the largest difficulty value possible:
arith_uint256 largest_difficulty_target;
largest_difficulty_target.SetCompact(old_nbits);
largest_difficulty_target *= largest_timespan;
largest_difficulty_target /= params.nPowTargetTimespan;
if (largest_difficulty_target > pow_limit) {
largest_difficulty_target = pow_limit;
}
// Round and then compare this new calculated value to what is
// observed.
arith_uint256 maximum_new_target;
maximum_new_target.SetCompact(largest_difficulty_target.GetCompact());
if (maximum_new_target < observed_new_target) return false;
// Calculate the smallest difficulty value possible:
arith_uint256 smallest_difficulty_target;
smallest_difficulty_target.SetCompact(old_nbits);
smallest_difficulty_target *= smallest_timespan;
smallest_difficulty_target /= params.nPowTargetTimespan;
if (smallest_difficulty_target > pow_limit) {
smallest_difficulty_target = pow_limit;
}
// Round and then compare this new calculated value to what is
// observed.
arith_uint256 minimum_new_target;
minimum_new_target.SetCompact(smallest_difficulty_target.GetCompact());
if (minimum_new_target > observed_new_target) return false;
} else if (old_nbits != new_nbits) {
return false;
}
return true;
}
bool CheckProofOfWork(uint256 hash, unsigned int nBits, const Consensus::Params& params)
{
bool fNegative;