Add fixed multi-algo chainwork scaling
This commit is contained in:
parent
0411c96b45
commit
ef32ac43a6
4 changed files with 170 additions and 5 deletions
|
|
@ -178,6 +178,54 @@ arith_uint256 GetBlockProof(const CBlockIndex& block)
|
|||
return GetBlockProofBase(block);
|
||||
}
|
||||
|
||||
static arith_uint256 ScaleMultiAlgoChainwork(
|
||||
const arith_uint256& work,
|
||||
uint32_t numerator,
|
||||
uint32_t denominator)
|
||||
{
|
||||
if (work == 0 || numerator == 0 || denominator == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (numerator == denominator) {
|
||||
return work;
|
||||
}
|
||||
|
||||
// Compute floor(work * numerator / denominator) without first
|
||||
// multiplying the full 256-bit value. base_uint multiplication silently
|
||||
// truncates on overflow, so split work into quotient and remainder:
|
||||
//
|
||||
// work = quotient * denominator + remainder
|
||||
//
|
||||
// Then:
|
||||
//
|
||||
// floor(work * numerator / denominator)
|
||||
// = quotient * numerator
|
||||
// + floor(remainder * numerator / denominator)
|
||||
//
|
||||
// remainder is smaller than the 32-bit denominator, so its product with
|
||||
// the 32-bit numerator fits comfortably inside 256 bits.
|
||||
arith_uint256 quotient = work / denominator;
|
||||
arith_uint256 remainder = work - quotient * denominator;
|
||||
|
||||
const arith_uint256 maxValue = ~arith_uint256(0);
|
||||
|
||||
if (quotient > maxValue / numerator) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
quotient *= numerator;
|
||||
|
||||
remainder *= numerator;
|
||||
remainder /= denominator;
|
||||
|
||||
if (quotient > maxValue - remainder) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return quotient + remainder;
|
||||
}
|
||||
|
||||
arith_uint256 GetBlockProof(
|
||||
const CBlockIndex& block,
|
||||
const Consensus::Params& params)
|
||||
|
|
@ -238,11 +286,12 @@ arith_uint256 GetBlockProof(
|
|||
arith_uint256 normalizedWork =
|
||||
(~averageTarget / (averageTarget + 1)) + 1;
|
||||
|
||||
// The normalized work value itself is the post-activation chainwork
|
||||
// increment. It is derived only from consensus difficulty state and does
|
||||
// not use a branch-specific activation anchor, so competing forks always
|
||||
// use the same chainwork scale.
|
||||
return normalizedWork;
|
||||
// Apply a fixed, branch-independent consensus scale. The default 1/1
|
||||
// preserves the normalized-work value exactly.
|
||||
return ScaleMultiAlgoChainwork(
|
||||
normalizedWork,
|
||||
params.multiAlgoChainworkScaleNumerator,
|
||||
params.multiAlgoChainworkScaleDenominator);
|
||||
}
|
||||
|
||||
int64_t GetBlockProofEquivalentTime(const CBlockIndex& to, const CBlockIndex& from,
|
||||
|
|
|
|||
|
|
@ -185,6 +185,8 @@ public:
|
|||
consensus.fastRiseActivationHeight = 7000;
|
||||
consensus.randomXActivationHeight = std::numeric_limits<int>::max();
|
||||
consensus.randomXInitialTarget = uint256S("000000359bc10000000000000000000000000000000000000000000000000000");
|
||||
consensus.multiAlgoChainworkScaleNumerator = 1;
|
||||
consensus.multiAlgoChainworkScaleDenominator = 1;
|
||||
consensus.randomXSeedEpochLength = 2048;
|
||||
consensus.randomXSeedLag = 64;
|
||||
|
||||
|
|
@ -231,6 +233,8 @@ public:
|
|||
consensus.fastRiseActivationHeight = 7000;
|
||||
consensus.randomXActivationHeight = std::numeric_limits<int>::max();
|
||||
consensus.randomXInitialTarget = consensus.powLimit;
|
||||
consensus.multiAlgoChainworkScaleNumerator = 1;
|
||||
consensus.multiAlgoChainworkScaleDenominator = 1;
|
||||
consensus.randomXSeedEpochLength = 2048;
|
||||
consensus.randomXSeedLag = 64;
|
||||
|
||||
|
|
@ -389,6 +393,8 @@ public:
|
|||
consensus.MinBIP9WarningHeight = 0;
|
||||
consensus.powLimit = uint256S("00000377ae000000000000000000000000000000000000000000000000000000");
|
||||
consensus.randomXInitialTarget = consensus.powLimit;
|
||||
consensus.multiAlgoChainworkScaleNumerator = 1;
|
||||
consensus.multiAlgoChainworkScaleDenominator = 1;
|
||||
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit = 27;
|
||||
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = Consensus::BIP9Deployment::NEVER_ACTIVE;
|
||||
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;
|
||||
|
|
@ -474,6 +480,8 @@ public:
|
|||
consensus.fastRiseActivationHeight = 7000;
|
||||
consensus.randomXActivationHeight = std::numeric_limits<int>::max();
|
||||
consensus.randomXInitialTarget = consensus.powLimit;
|
||||
consensus.multiAlgoChainworkScaleNumerator = 1;
|
||||
consensus.multiAlgoChainworkScaleDenominator = 1;
|
||||
consensus.randomXSeedEpochLength = 64;
|
||||
consensus.randomXSeedLag = 8;
|
||||
|
||||
|
|
|
|||
|
|
@ -126,6 +126,14 @@ struct Params {
|
|||
int fastRiseActivationHeight;
|
||||
int randomXActivationHeight;
|
||||
uint256 randomXInitialTarget;
|
||||
|
||||
// Fixed, branch-independent scaling applied to normalized
|
||||
// multi-algo chainwork after RandomX activation.
|
||||
//
|
||||
// A value of 1/1 preserves the current normalized-work scale.
|
||||
uint32_t multiAlgoChainworkScaleNumerator{1};
|
||||
uint32_t multiAlgoChainworkScaleDenominator{1};
|
||||
|
||||
int randomXSeedEpochLength;
|
||||
int randomXSeedLag;
|
||||
|
||||
|
|
|
|||
|
|
@ -153,6 +153,106 @@ BOOST_AUTO_TEST_CASE(MultiAlgo_chainwork_normalization_test)
|
|||
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(MultiAlgo_chainwork_scale_test)
|
||||
{
|
||||
auto chainParams = CreateChainParams(*m_node.args, CBaseChainParams::MAIN);
|
||||
auto consensus = chainParams->GetConsensus();
|
||||
|
||||
consensus.randomXActivationHeight = 2;
|
||||
|
||||
CBlockIndex genesisLike;
|
||||
genesisLike.nHeight = 0;
|
||||
genesisLike.nTime = 1800000000;
|
||||
genesisLike.nBits =
|
||||
UintToArith256(consensus.powLimit).GetCompact();
|
||||
genesisLike.nVersion =
|
||||
BLOCK_VERSION_DEFAULT | BLOCK_VERSION_SHA256D;
|
||||
|
||||
CBlockIndex parent;
|
||||
parent.pprev = &genesisLike;
|
||||
parent.nHeight = 1;
|
||||
parent.nTime =
|
||||
genesisLike.nTime + consensus.nPowTargetSpacing;
|
||||
parent.nBits =
|
||||
UintToArith256(consensus.powLimit).GetCompact();
|
||||
parent.nVersion =
|
||||
BLOCK_VERSION_DEFAULT | BLOCK_VERSION_SHA256D;
|
||||
parent.lastAlgoBlocks[ALGO_SHA256D] = &genesisLike;
|
||||
|
||||
CBlockIndex child;
|
||||
child.pprev = &parent;
|
||||
child.nHeight = 2;
|
||||
child.nTime =
|
||||
parent.nTime + consensus.nPowTargetSpacing;
|
||||
child.nVersion =
|
||||
BLOCK_VERSION_DEFAULT | BLOCK_VERSION_SHA256D;
|
||||
child.nBits =
|
||||
GetNextWorkRequired(
|
||||
&parent,
|
||||
nullptr,
|
||||
consensus,
|
||||
ALGO_SHA256D);
|
||||
|
||||
consensus.multiAlgoChainworkScaleNumerator = 1;
|
||||
consensus.multiAlgoChainworkScaleDenominator = 1;
|
||||
const arith_uint256 unscaled =
|
||||
GetBlockProof(child, consensus);
|
||||
|
||||
BOOST_REQUIRE(unscaled > 0);
|
||||
|
||||
consensus.multiAlgoChainworkScaleNumerator = 2;
|
||||
consensus.multiAlgoChainworkScaleDenominator = 1;
|
||||
BOOST_CHECK(
|
||||
GetBlockProof(child, consensus) ==
|
||||
unscaled * 2);
|
||||
|
||||
consensus.multiAlgoChainworkScaleNumerator = 1;
|
||||
consensus.multiAlgoChainworkScaleDenominator = 2;
|
||||
BOOST_CHECK(
|
||||
GetBlockProof(child, consensus) ==
|
||||
unscaled / 2);
|
||||
|
||||
consensus.multiAlgoChainworkScaleNumerator = 1;
|
||||
consensus.multiAlgoChainworkScaleDenominator = 0;
|
||||
BOOST_CHECK(
|
||||
GetBlockProof(child, consensus) == 0);
|
||||
|
||||
consensus.multiAlgoChainworkScaleNumerator = 0;
|
||||
consensus.multiAlgoChainworkScaleDenominator = 1;
|
||||
BOOST_CHECK(
|
||||
GetBlockProof(child, consensus) == 0);
|
||||
|
||||
// Force the overflow guard using consensus state that produces an
|
||||
// extremely large normalized work value. Post-activation chainwork is
|
||||
// derived from expected per-algo targets, not from child.nBits.
|
||||
auto overflowConsensus = consensus;
|
||||
overflowConsensus.fPowNoRetargeting = true;
|
||||
overflowConsensus.randomXActivationHeight = 2;
|
||||
|
||||
arith_uint256 tinyTarget(1);
|
||||
const unsigned int tinyBits = tinyTarget.GetCompact();
|
||||
overflowConsensus.randomXInitialTarget =
|
||||
ArithToUint256(tinyTarget);
|
||||
|
||||
CBlockIndex overflowParent = parent;
|
||||
overflowParent.nBits = tinyBits;
|
||||
overflowParent.nVersion =
|
||||
BLOCK_VERSION_DEFAULT | BLOCK_VERSION_SHA256D;
|
||||
|
||||
CBlockIndex overflowChild = child;
|
||||
overflowChild.pprev = &overflowParent;
|
||||
overflowChild.nHeight = 2;
|
||||
|
||||
overflowConsensus.multiAlgoChainworkScaleNumerator =
|
||||
std::numeric_limits<uint32_t>::max();
|
||||
overflowConsensus.multiAlgoChainworkScaleDenominator = 1;
|
||||
|
||||
BOOST_CHECK(
|
||||
GetBlockProof(overflowChild, overflowConsensus) == 0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(MultiAlgo_chainwork_branch_accumulation_test)
|
||||
{
|
||||
auto chainParams = CreateChainParams(*m_node.args, CBaseChainParams::MAIN);
|
||||
|
|
|
|||
Loading…
Reference in a new issue