Files
thehub/hub/server/pow.cpp
tomFlowee 0a2e552168 Move more logic over to byte-array based Block
This introduces a new BlockHeader helper class which Block and
MutableBlock can both produce, which helps a lot of methods to
be ported to no longer be dependent on us using a MutableBlock
object, which is too costly to use when we have no intention
to alter the block.
2026-04-20 22:21:18 +02:00

456 lines
17 KiB
C++

/*
* This file is part of the Flowee project
* Copyright (c) 2009-2010 Satoshi Nakamoto
* Copyright (c) 2009-2015 The Bitcoin Core developers
* Copyright (c) 2017-2020 The Bitcoin developers
* Copyright (c) 2017,2020 Tom Zander <tom@flowee.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "pow.h"
#include <Application.h>
#include "arith_uint256.h"
#include "chain.h"
#include "primitives/BlockHeader.h"
#include "primitives/MutableBlock.h"
#include "uint256.h"
#include "util.h"
#include <atomic>
static std::atomic<const CBlockIndex*> cachedAnchor{nullptr};
void ResetASERTAnchorBlockCache()
{
cachedAnchor.store(nullptr);
}
namespace {
/**
* Compute the next required proof of work using the legacy Satoshi Difficulty adjustment + Emergency Difficulty Adjustment (EDA).
*/
uint32_t GetNextEDAWorkRequired(const CBlockIndex *pindexPrev, const BlockHeader &header, const Consensus::Params &params)
{
// Only change once per difficulty adjustment interval
uint32_t nHeight = pindexPrev->nHeight + 1;
if (nHeight % params.DifficultyAdjustmentInterval() == 0) {
// Go back by what we want to be 14 days worth of blocks
assert(nHeight >= params.DifficultyAdjustmentInterval());
uint32_t nHeightFirst = nHeight - params.DifficultyAdjustmentInterval();
const CBlockIndex *pindexFirst = pindexPrev->GetAncestor(nHeightFirst);
assert(pindexFirst);
return Calculate2016NextWorkRequired(pindexPrev, pindexFirst->GetBlockTime(), params);
}
const uint32_t nProofOfWorkLimit = UintToArith256(params.powLimit).GetCompact();
if (params.fPowAllowMinDifficultyBlocks) {
// 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.
if (header.blockTime() > pindexPrev->GetBlockTime() + 2 * params.nPowTargetSpacing) {
return nProofOfWorkLimit;
}
// Return the last non-special-min-difficulty-rules-block
const CBlockIndex *pindex = pindexPrev;
while (pindex->pprev && pindex->nHeight % params.DifficultyAdjustmentInterval() != 0 && pindex->nBits == nProofOfWorkLimit) {
pindex = pindex->pprev;
}
return pindex->nBits;
}
// We can't go below the minimum, so bail early.
uint32_t nBits = pindexPrev->nBits;
if (nBits == nProofOfWorkLimit)
return nProofOfWorkLimit;
// If producing the last 6 blocks took less than 12h, we keep the same
// difficulty.
const CBlockIndex *pindex6 = pindexPrev->GetAncestor(nHeight - 7);
assert(pindex6);
int64_t mtp6blocks = pindexPrev->GetMedianTimePast() - pindex6->GetMedianTimePast();
if (mtp6blocks < 12 * 3600)
return nBits;
// If producing the last 6 blocks took more than 12h, increase the
// difficulty target by 1/4 (which reduces the difficulty by 20%).
// This ensures that the chain does not get stuck in case we lose
// hashrate abruptly.
arith_uint256 nPow;
nPow.SetCompact(nBits);
nPow += (nPow >> 2);
// Make sure we do not go below allowed values.
const arith_uint256 bnPowLimit = UintToArith256(params.powLimit);
if (nPow > bnPowLimit)
nPow = bnPowLimit;
return nPow.GetCompact();
}
/**
* Compute a target based on the work done between 2 blocks and the time
* required to produce that work.
*/
arith_uint256 ComputeTarget(const CBlockIndex *pindexFirst, const CBlockIndex *pindexLast, const Consensus::Params &params)
{
assert(pindexLast->nHeight > pindexFirst->nHeight);
/**
* From the total work done and the time it took to produce that much work,
* we can deduce how much work we expect to be produced in the targeted time
* between blocks.
*/
arith_uint256 work = pindexLast->nChainWork - pindexFirst->nChainWork;
work *= params.nPowTargetSpacing;
// In order to avoid difficulty cliffs, we bound the amplitude of the
// adjustment we are going to do to a factor in [0.5, 2].
int64_t nActualTimespan =
int64_t(pindexLast->nTime) - int64_t(pindexFirst->nTime);
if (nActualTimespan > 288 * params.nPowTargetSpacing) {
nActualTimespan = 288 * params.nPowTargetSpacing;
} else if (nActualTimespan < 72 * params.nPowTargetSpacing) {
nActualTimespan = 72 * params.nPowTargetSpacing;
}
work /= nActualTimespan;
/**
* We need to compute T = (2^256 / W) - 1 but 2^256 doesn't fit in 256 bits.
* By expressing 1 as W / W, we get (2^256 - W) / W, and we can compute
* 2^256 - W as the complement of W.
*/
return (-work) / work;
}
const CBlockIndex *GetASERTAnchorBlock(const CBlockIndex *const pindex, const Consensus::Params &params)
{
assert(pindex);
const CBlockIndex *cached = cachedAnchor.load();
if (cached)
return cached;
assert(params.hf202011Height > 0);
const CBlockIndex *anchor = pindex->GetAncestor(params.hf202011Height - 1);
cachedAnchor.compare_exchange_strong(cached, anchor);
return anchor;
}
}
/**
* To reduce the impact of timestamp manipulation, we select the block we are
* basing our computation on via a median of 3.
*/
const CBlockIndex *GetSuitableBlock(const CBlockIndex *pindex)
{
assert(pindex->nHeight >= 3);
/**
* In order to avoid a block with a very skewed timestamp having too much
* influence, we select the median of the 3 top most blocks as a starting
* point.
*/
const CBlockIndex *blocks[3];
blocks[2] = pindex;
blocks[1] = pindex->pprev;
blocks[0] = blocks[1]->pprev;
// Sorting network.
if (blocks[0]->nTime > blocks[2]->nTime) {
std::swap(blocks[0], blocks[2]);
}
if (blocks[0]->nTime > blocks[1]->nTime) {
std::swap(blocks[0], blocks[1]);
}
if (blocks[1]->nTime > blocks[2]->nTime) {
std::swap(blocks[1], blocks[2]);
}
// We should have our candidate in the middle now.
return blocks[1];
}
uint32_t CalculateNextWorkRequired(const CBlockIndex *pindexPrev, const BlockHeader &header, const Consensus::Params &params)
{
assert (pindexPrev); // Genesis block not allowed.
// Special rule for regtest: we never retarget.
if (params.fPowNoRetargeting)
return pindexPrev->nBits;
if (pindexPrev->nHeight >= params.hf201711Height) {
if (pindexPrev->nHeight + 1 >= params.hf202011Height)
return CalculateNextASERTWorkRequired(pindexPrev, header, params,
GetASERTAnchorBlock(pindexPrev, params));
// then the 3 year period of cw144
return CalculateNextCW144WorkRequired(pindexPrev, header, params);
}
// the couple of months of the emergency difficulty adjustment algo
return GetNextEDAWorkRequired(pindexPrev, header, params);
}
uint32_t CalculateNextASERTWorkRequired(const CBlockIndex *pindexPrev,
const BlockHeader &header,
const Consensus::Params &params,
const CBlockIndex *pindexAnchorBlock)
{
// This cannot handle the genesis block and early blocks in general.
assert(pindexPrev != nullptr);
// Anchor block is the block on which all ASERT scheduling calculations are based.
// It too must exist, and it must have a valid parent.
assert(pindexAnchorBlock != nullptr);
// We make no further assumptions other than the height of the prev block must be >= that of the anchor block.
assert(pindexPrev->nHeight >= pindexAnchorBlock->nHeight);
const arith_uint256 powLimit = UintToArith256(params.powLimit);
// 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.
if (params.fPowAllowMinDifficultyBlocks &&
(header.blockTime() >
pindexPrev->GetBlockTime() + 2 * params.nPowTargetSpacing)) {
return UintToArith256(params.powLimit).GetCompact();
}
// For nTimeDiff calculation, the timestamp of the parent to the anchor block is used,
// as per the absolute formulation of ASERT.
// This is somewhat counterintuitive since it is referred to as the anchor timestamp, but
// as per the formula the timestamp of block M-1 must be used if the anchor is M.
assert(pindexPrev->pprev != nullptr);
// Note: time difference is to parent of anchor block (or to anchor block itself if anchor is genesis).
// (according to absolute formulation of ASERT)
const auto anchorTime = pindexAnchorBlock->pprev
? pindexAnchorBlock->pprev->GetBlockTime()
: pindexAnchorBlock->GetBlockTime();
const int64_t nTimeDiff = pindexPrev->GetBlockTime() - anchorTime;
// Height difference is from current block to anchor block
const int64_t nHeightDiff = pindexPrev->nHeight - pindexAnchorBlock->nHeight;
const arith_uint256 refBlockTarget = arith_uint256().SetCompact(pindexAnchorBlock->nBits);
// Do the actual target adaptation calculation in separate
// CalculateASERT() function
arith_uint256 nextTarget = CalculateASERT(refBlockTarget, params.nPowTargetSpacing,
nTimeDiff, nHeightDiff, powLimit, params.nASERTHalfLife);
// CalculateASERT() already clamps to powLimit.
return nextTarget.GetCompact();
}
/// ASERT calculation function.
arith_uint256 CalculateASERT(const arith_uint256 &refTarget, const int64_t nPowTargetSpacing, const int64_t nTimeDiff,
const int64_t nHeightDiff, const arith_uint256 &powLimit, const int64_t nHalfLife) noexcept
{
// Our anchor block, or reference block, has to have a sane PoW target.
assert(refTarget > 0 && refTarget <= powLimit);
// We need some leading zero bits in powLimit in order to have room to handle
// overflows easily. 32 leading zero bits is more than enough.
assert((powLimit >> 224) == 0);
// We can only calculate blocks that are appended after the anchor block.
assert(nHeightDiff >= 0);
// Sane chain-config (not regtest)
assert(nHalfLife > 0);
// It will be helpful when reading what follows, to remember that
// nextTarget is adapted from anchor block target value.
// Ultimately, we want to approximate the following ASERT formula, using only integer (fixed-point) math:
// new_target = old_target * 2^((blocks_time - IDEAL_BLOCK_TIME * (height_diff + 1)) / nHalfLife)
// First, we'll calculate the exponent:
assert( llabs(nTimeDiff - nPowTargetSpacing * nHeightDiff) < (1ll << (63 - 16)) );
const int64_t exponent = ((nTimeDiff - nPowTargetSpacing * (nHeightDiff + 1)) * 65536) / nHalfLife;
// Next, we use the 2^x = 2 * 2^(x-1) identity to shift our exponent into the [0, 1) interval.
// The truncated exponent tells us how many shifts we need to do
// Note1: This needs to be a right shift. Right shift rounds downward (floored division),
// whereas integer division in C++ rounds towards zero (truncated division).
// Note2: This algorithm uses arithmetic shifts of negative numbers. This
// is unpecified but very common behavior for C++ compilers before
// C++20, and standard with C++20. We must check this behavior e.g.
// using static_assert.
static_assert(int64_t(-1) >> 1 == int64_t(-1),
"ASERT algorithm needs arithmetic shift support");
// Now we compute an approximated target * 2^(exponent/65536.0)
// First decompose exponent into 'integer' and 'fractional' parts:
int64_t shifts = exponent >> 16;
const auto frac = uint16_t(exponent);
assert(exponent == (shifts * 65536) + frac);
// multiply target by 65536 * 2^(fractional part)
// 2^x ~= (1 + 0.695502049*x + 0.2262698*x**2 + 0.0782318*x**3) for 0 <= x < 1
// Error versus actual 2^x is less than 0.013%.
const uint32_t factor = 65536 + ((
+ 195766423245049ull * frac
+ 971821376ull * frac * frac
+ 5127ull * frac * frac * frac
+ (1ull << 47)
) >> 48);
// this is always < 2^241 since refTarget < 2^224
arith_uint256 nextTarget = refTarget * factor;
// multiply by 2^(integer part) / 65536
shifts -= 16;
if (shifts <= 0) {
nextTarget >>= -shifts;
} else {
// Predetect overflow that would silently discard high bits.
if (int64_t(nextTarget.bits()) + shifts > 255) {
// If we had wider integers, the final value of nextTarget would be
// >= 2^256 so it would have just ended up as powLimit anyway.
return powLimit;
}
nextTarget <<= shifts;
}
if (nextTarget == 0) {
// 0 is not a valid target, but 1 is.
return arith_uint256(1);
}
if (nextTarget > powLimit) {
return powLimit;
}
return nextTarget;
}
uint32_t CalculateNextCW144WorkRequired(const CBlockIndex *pindexPrev, const BlockHeader &header, const Consensus::Params &params)
{
// This cannot handle the genesis block and early blocks in general.
assert(pindexPrev);
// 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.
if (params.fPowAllowMinDifficultyBlocks
&& (header.blockTime() > pindexPrev->GetBlockTime() + 2 * params.nPowTargetSpacing))
return UintToArith256(params.powLimit).GetCompact();
// Compute the difficulty based on the full adjustment interval.
const int nHeight = pindexPrev->nHeight;
assert(nHeight >= params.DifficultyAdjustmentInterval());
// Get the last suitable block of the difficulty interval.
const CBlockIndex *pindexLast = GetSuitableBlock(pindexPrev);
assert(pindexLast);
// Get the first suitable block of the difficulty interval.
const int nHeightFirst = nHeight - 144;
const CBlockIndex *pindexFirst = GetSuitableBlock(pindexPrev->GetAncestor(nHeightFirst));
assert(pindexFirst);
// Compute the target based on time and work done during the interval.
const arith_uint256 nextTarget = ComputeTarget(pindexFirst, pindexLast, params);
const arith_uint256 powLimit = UintToArith256(params.powLimit);
if (nextTarget > powLimit)
return powLimit.GetCompact();
return nextTarget.GetCompact();
}
// Satoshi's algo
uint32_t Calculate2016NextWorkRequired(const CBlockIndex *pindexPrev, int64_t nFirstBlockTime, const Consensus::Params &params)
{
if (params.fPowNoRetargeting)
return pindexPrev->nBits;
// Limit adjustment step
int64_t nActualTimespan = pindexPrev->GetBlockTime() - nFirstBlockTime;
logDebug(Log::Bitcoin) << "nActualTimespan =" << nActualTimespan << "before bounds";
if (nActualTimespan < params.nPowTargetTimespan/4)
nActualTimespan = params.nPowTargetTimespan/4;
if (nActualTimespan > params.nPowTargetTimespan*4)
nActualTimespan = params.nPowTargetTimespan*4;
// Retarget
const arith_uint256 bnPowLimit = UintToArith256(params.powLimit);
arith_uint256 bnNew;
bnNew.SetCompact(pindexPrev->nBits);
bnNew *= nActualTimespan;
bnNew /= params.nPowTargetTimespan;
if (bnNew > bnPowLimit)
bnNew = bnPowLimit;
return bnNew.GetCompact();
}
bool CheckProofOfWork(const uint256 &hash, uint32_t nBits, const Consensus::Params &params) {
bool fNegative;
bool fOverflow;
arith_uint256 bnTarget;
bnTarget.SetCompact(nBits, &fNegative, &fOverflow);
// Check range
if (fNegative || bnTarget == 0 || fOverflow || bnTarget > UintToArith256(params.powLimit))
return false;
// Check proof of work matches claimed amount
if (UintToArith256(hash) > bnTarget)
return false;
return true;
}
arith_uint256 GetBlockProof(const CBlockIndex& block)
{
arith_uint256 bnTarget;
bool fNegative;
bool fOverflow;
bnTarget.SetCompact(block.nBits, &fNegative, &fOverflow);
if (fNegative || fOverflow || bnTarget == 0)
return 0;
// We need to compute 2**256 / (bnTarget+1), but we can't represent 2**256
// as it's too large for a arith_uint256. However, as 2**256 is at least as large
// as bnTarget+1, it is equal to ((2**256 - bnTarget - 1) / (bnTarget+1)) + 1,
// or ~bnTarget / (nTarget+1) + 1.
return (~bnTarget / (bnTarget + 1)) + 1;
}
int64_t GetBlockProofEquivalentTime(const CBlockIndex& to, const CBlockIndex& from, const CBlockIndex& tip, const Consensus::Params& params)
{
arith_uint256 r;
int sign = 1;
if (to.nChainWork > from.nChainWork) {
r = to.nChainWork - from.nChainWork;
} else {
r = from.nChainWork - to.nChainWork;
sign = -1;
}
r = r * arith_uint256(params.nPowTargetSpacing) / GetBlockProof(tip);
if (r.bits() > 63) {
return sign * std::numeric_limits<int64_t>::max();
}
return sign * r.GetLow64();
}