/// <summary> /// Checks that the nBits field of a block follow protocol rules. /// </summary> internal static bool IsNBitsValid(StoredBlock block, Subchain parentChain) { //todo: add test for this method StoredBlock parentBlock = parentChain.GetBlockByOffset(0); BigInteger blockDifficultyTarget = DifficultyUtils.NBitsToTarget(block.Header.NBits); if (block.Height%DifficultyUtils.DifficultyAdjustmentIntervalInBlocks != 0) { BigInteger parentBlockDifficultyTarget = DifficultyUtils.NBitsToTarget(parentBlock.Header.NBits); if (blockDifficultyTarget != parentBlockDifficultyTarget) { return false; } } else { StoredBlock baseBlock = parentChain.GetBlockByHeight(block.Height - DifficultyUtils.DifficultyAdjustmentIntervalInBlocks); // todo: check against non-bugged value and allow some percent-based difference? // Note: There is a known bug here. It known as "off-by-one" or "Time Warp Bug". // Bug Description: http://bitcoin.stackexchange.com/questions/20597/where-exactly-is-the-off-by-one-difficulty-bug // Related Attack Description: https://bitcointalk.org/index.php?topic=43692.msg521772#msg521772 uint timeSpent = parentBlock.Header.Timestamp - baseBlock.Header.Timestamp; BigInteger oldTarget = DifficultyUtils.NBitsToTarget(parentBlock.Header.NBits); var newTarget = DifficultyUtils.CalculateNewTarget(timeSpent, oldTarget); // this rounds newTarget value to a value that would fit into NBits uint newNBits = DifficultyUtils.TargetToNBits(newTarget); newTarget = DifficultyUtils.NBitsToTarget(newNBits); if (blockDifficultyTarget != newTarget) { return false; } } return true; }