diff --git a/src/chain.cpp b/src/chain.cpp index 79e8bdfa44..ff44aef1b0 100644 --- a/src/chain.cpp +++ b/src/chain.cpp @@ -4,6 +4,7 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include +#include "chainparams.h" /** * CChain implementation @@ -118,7 +119,79 @@ void CBlockIndex::BuildSkip() pskip = pprev->GetAncestor(GetSkipHeight(nHeight)); } -arith_uint256 GetBlockProof(const CBlockIndex& block) +arith_uint256 uint256_nthRoot(const int root, const arith_uint256 bn) +{ + assert(root > 1); + if (bn==0) + return 0; + assert(bn > 0); + + // starting approximation + int nRootBits = (bn.bits() + root - 1) / root; + int nStartingBits = std::min(8, nRootBits); + arith_uint256 bnUpper = bn; + bnUpper >>= (nRootBits - nStartingBits)*root; + arith_uint256 bnCur = 0; + for (int i = nStartingBits - 1; i >= 0; i--) { + arith_uint256 bnNext = bnCur; + bnNext += 1 << i; + arith_uint256 bnPower = 1; + for (int j = 0; j < root; j++) + bnPower *= bnNext; + if (bnPower <= bnUpper) + bnCur = bnNext; + } + if (nRootBits == nStartingBits) + return bnCur; + bnCur <<= nRootBits - nStartingBits; + + // iterate: cur = cur + (bn / cur^^(root-1) - cur)/root + arith_uint256 bnDelta; + const arith_uint256 bnRoot = root; + int nTerminate = 0; + bool fNegativeDelta = false; + // this should always converge in fewer steps, but limit just in case + for (int it = 0; it < 20; it++) + { + arith_uint256 bnDenominator = 1; + for (int i = 0; i < root - 1; i++) + bnDenominator *= bnCur; + if (bnCur > bn/bnDenominator) + fNegativeDelta = true; + if (bnCur == bn/bnDenominator) // bnDelta=0 + return bnCur; + if (fNegativeDelta) { + bnDelta = bnCur - bn/bnDenominator; + if (nTerminate == 1) + return bnCur - 1; + fNegativeDelta = false; + if (bnDelta <= bnRoot) { + bnCur -= 1; + nTerminate = -1; + continue; + } + fNegativeDelta = true; + } else { + bnDelta = bn/bnDenominator - bnCur; + if (nTerminate == -1) + return bnCur; + if (bnDelta <= bnRoot) { + bnCur += 1; + nTerminate = 1; + continue; + } + } + if (fNegativeDelta) { + bnCur -= bnDelta / bnRoot; + } else { + bnCur += bnDelta / bnRoot; + } + nTerminate = 0; + } + return bnCur; +} + +arith_uint256 GetBlockProofBase(const CBlockIndex& block) { arith_uint256 bnTarget; bool fNegative; @@ -133,6 +206,77 @@ arith_uint256 GetBlockProof(const CBlockIndex& block) return (~bnTarget / (bnTarget + 1)) + 1; } +arith_uint256 GetPrevWorkForAlgoWithDecay(const CBlockIndex& block, int algo) +{ + int nDistance = 0; + arith_uint256 nWork; + const CBlockIndex* pindex = █ + while (pindex != NULL) + { + if (nDistance > 100) + { + return arith_uint256(0); + } + if (pindex->GetAlgo() == algo) + { + arith_uint256 nWork = GetBlockProofBase(*pindex); + nWork *= (100 - nDistance); + nWork /= 100; + return nWork; + } + pindex = pindex->pprev; + nDistance++; + } + return arith_uint256(0); +} + +arith_uint256 GetGeometricMeanPrevWork(const CBlockIndex& block) +{ + arith_uint256 bnRes; + arith_uint256 nBlockWork = GetBlockProofBase(block); + int nAlgo = block.GetAlgo(); + + // Compute the geometric mean + // We use the nthRoot product rule here: + // nthRoot(a*b*...) = nthRoot(a)*nthRoot(b)*... + // This is to ensure we never overflow a uint256. + nBlockWork = uint256_nthRoot(NUM_ALGOS, nBlockWork); + + for (int algo = 0; algo < NUM_ALGOS_IMPL; algo++) + { + if (algo != nAlgo) + { + arith_uint256 nBlockWorkAlt = GetPrevWorkForAlgoWithDecay(block, algo); + if (nBlockWorkAlt != 0) + nBlockWork *= uint256_nthRoot(NUM_ALGOS,nBlockWorkAlt); // Again, the nthRoot product rule. + } + } + // In the past we have computed the geometric mean here, + // but do not need to from the nthRoot product rule above. + bnRes = nBlockWork; + + // Scale to roughly match the old work calculation + bnRes <<= 8; + + return bnRes; + +} + +arith_uint256 GetBlockProof(const CBlockIndex& block) +{ + Consensus::Params params = Params().GetConsensus(); + int nHeight = block.nHeight; + + if(nHeight > params.nStartMultiAlgoHash) + { + return GetGeometricMeanPrevWork(block); + } + else + { + return GetBlockProofBase(block); + } +} + int64_t GetBlockProofEquivalentTime(const CBlockIndex& to, const CBlockIndex& from, const CBlockIndex& tip, const Consensus::Params& params) { arith_uint256 r; @@ -168,3 +312,15 @@ const CBlockIndex* LastCommonAncestor(const CBlockIndex* pa, const CBlockIndex* assert(pa == pb); return pa; } + +const CBlockIndex* GetLastBlockIndexForAlgo(const CBlockIndex* pindex, int algo) +{ + for (;;) + { + if (!pindex) + return NULL; + if (pindex->GetAlgo() == algo) + return pindex; + pindex = pindex->pprev; + } +} \ No newline at end of file diff --git a/src/chain.h b/src/chain.h index 8ccf9c9127..045bcce4d2 100644 --- a/src/chain.h +++ b/src/chain.h @@ -294,9 +294,14 @@ class CBlockIndex return *phashBlock; } - uint256 GetBlockPoWHash() const + uint256 GetBlockPoWHash(const Consensus::Params& params) const { - return GetBlockHeader().GetPoWHash(nHeight); + return GetBlockHeader().GetPoWHash(nHeight, params); + } + + int GetAlgo() const + { + return ::GetAlgo(nVersion); } int64_t GetBlockTime() const @@ -369,7 +374,8 @@ arith_uint256 GetBlockProof(const CBlockIndex& block); int64_t GetBlockProofEquivalentTime(const CBlockIndex& to, const CBlockIndex& from, const CBlockIndex& tip, const Consensus::Params&); /** Find the forking point between two chain tips. */ const CBlockIndex* LastCommonAncestor(const CBlockIndex* pa, const CBlockIndex* pb); - +/** Return the index to the last block of algo */ +const CBlockIndex* GetLastBlockIndexForAlgo(const CBlockIndex* pindex, int algo); /** Used to marshal pointers into hashes for db storage. */ class CDiskBlockIndex : public CBlockIndex diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 7964b2c8c1..7725bdbdbf 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -109,6 +109,18 @@ class CMainParams : public CChainParams { // By default assume that the signatures in ancestors of this block are valid. consensus.defaultAssumeValid = uint256S("0x4b151d928c0aae106c9d69347df59e0088cbd33dd659deab126506865a8b0060"); //898726 + consensus.nStartLyra2reHash = 208301; // height where lyra2re replaced scrypt-n + consensus.nStartLyra2re2Hash = 347000; // height where lyra2re2 replaced lyra2re + consensus.nStartLyra2re3Hash = 1080000; // height where lyra2re3 replaced lyra2re2 + consensus.nStartMultiAlgoHash = 10000000; // height where multi-algorithm is active - UPDATE + + consensus.nStartKGWWorkCalc = 26754; // height where KimotoGravityWell replaces bitcoin's difficulty algorithm + + // multishield parameters + consensus.nAveragingInterval = 10; // 10 blocks + consensus.nMaxAdjustDown = 16; // 16% adjustment down + consensus.nMaxAdjustUp = 8; // 8% adjustment up + /** * The message start string is designed to be unlikely to occur in normal data. * The characters are rarely used upper ASCII, not valid as UTF-8, and produce @@ -181,7 +193,7 @@ class CTestNetParams : public CChainParams { strNetworkID = "test"; consensus.testnet = true; consensus.nSubsidyHalvingInterval = 840000; - consensus.BIP16Height = 0; + consensus.BIP16Height = 0; consensus.powLimit = uint256S("00000fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); consensus.nPowTargetTimespan = 3.5 * 24 * 60 * 60; // two weeks consensus.nPowTargetSpacing = 2.5 * 60; @@ -193,17 +205,17 @@ class CTestNetParams : public CChainParams { // Deployment of BIP65, BIP66, and BIP34. consensus.vDeployments[Consensus::DEPLOYMENT_NVERSIONBIPS].bit = 2; consensus.vDeployments[Consensus::DEPLOYMENT_NVERSIONBIPS].nStartTime = 1486865123; - consensus.vDeployments[Consensus::DEPLOYMENT_NVERSIONBIPS].nTimeout = 1517356801; + consensus.vDeployments[Consensus::DEPLOYMENT_NVERSIONBIPS].nTimeout = 1517356801; // Deployment of BIP68, BIP112, and BIP113. consensus.vDeployments[Consensus::DEPLOYMENT_CSV].bit = 0; - consensus.vDeployments[Consensus::DEPLOYMENT_CSV].nStartTime = 1486865123; - consensus.vDeployments[Consensus::DEPLOYMENT_CSV].nTimeout = 1517356801; + consensus.vDeployments[Consensus::DEPLOYMENT_CSV].nStartTime = 1486865123; + consensus.vDeployments[Consensus::DEPLOYMENT_CSV].nTimeout = 1517356801; // Deployment of SegWit (BIP141, BIP143, and BIP147) consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].bit = 1; consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nStartTime = 1486865123; - consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nTimeout = 1517356801; + consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nTimeout = 1517356801; // The best chain should have at least this much work. consensus.nMinimumChainWork = uint256S("0x0000000000000000000000000000000000000000000000000000000000100010"); @@ -211,6 +223,19 @@ class CTestNetParams : public CChainParams { // By default assume that the signatures in ancestors of this block are valid. consensus.defaultAssumeValid = uint256S("0x69ab56f74d75afa90b65b1fe10df8adaf2769e2ba64df1e1dc99c4d6717e1a2a"); //9000 + consensus.nStartLyra2reHash = 0; // height where lyra2re replaced scrypt-n + consensus.nStartLyra2re2Hash = 0; // height where lyra2re2 replaced lyra2re + consensus.nStartLyra2re3Hash = 158220; // height where lyra2re3 replaced lyra2re2 + consensus.nStartMultiAlgoHash = 210000; // height where multi-algorithm is active - UPDATE + + consensus.nStartKGWWorkCalc = 2116; // height where KimotoGravityWell replaces bitcoin's difficulty algorithm + + // multishield parameters + consensus.nAveragingInterval = 10; // 10 blocks + consensus.nMaxAdjustDown = 16; // 16% adjustment down + consensus.nMaxAdjustUp = 8; // 8% adjustment up + consensus.nLocalTargetAdjustment = 4; //target adjustment per algo + pchMessageStart[0] = 'v'; pchMessageStart[1] = 'e'; pchMessageStart[2] = 'r'; diff --git a/src/consensus/params.h b/src/consensus/params.h index acf7ecd8ec..c9411560b7 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -70,6 +70,16 @@ struct Params { uint256 nMinimumChainWork; uint256 defaultAssumeValid; bool testnet; + + int nStartLyra2reHash; + int nStartLyra2re2Hash; + int nStartLyra2re3Hash; + int nStartMultiAlgoHash; + int nStartKGWWorkCalc; + int nAveragingInterval; + int nMaxAdjustDown; + int nMaxAdjustUp; + int nLocalTargetAdjustment; }; } // namespace Consensus diff --git a/src/init.cpp b/src/init.cpp index 7bcb98a956..c2a74f472a 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -521,7 +521,7 @@ std::string LicenseInfo() const std::string URL_SOURCE_CODE = ""; const std::string URL_WEBSITE = ""; - return CopyrightHolders(strprintf(_("Copyright (C) %i-%i"), 2014, COPYRIGHT_YEAR) + " ", + return CopyrightHolders(strprintf(_("Copyright (C) %i-%i"), 2014, COPYRIGHT_YEAR) + " ", strprintf(_("Copyright (C) %i-%i"), 2009, COPYRIGHT_YEAR) + " ") + "\n" + "\n" + strprintf(_("Please contribute if you find %s useful. " @@ -1156,6 +1156,19 @@ bool AppInitParameterInteraction() } } } + + // Set Mining Algorithm + std::string strAlgo = gArgs.GetArg("-algo", "lyra2rev3"); + transform(strAlgo.begin(),strAlgo.end(),strAlgo.begin(),::tolower); + if (strAlgo == "lyra2rev3" || strAlgo == "lyra2re3" || strAlgo == "lyra2re") + miningAlgo = ALGO_LYRA2REV3; + else if (strAlgo == "newalgo1") + miningAlgo = ALGO_NEWALGO1; + else if (strAlgo == "newalgo2") + miningAlgo = ALGO_NEWALGO2; + else + miningAlgo = ALGO_LYRA2REV3; + return true; } diff --git a/src/miner.cpp b/src/miner.cpp index dda52790c6..8463b6e7a3 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -54,7 +54,7 @@ int64_t UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParam // Updating time can change work required on testnet: if (consensusParams.fPowAllowMinDifficultyBlocks) - pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, consensusParams); + pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, consensusParams, pblock->GetAlgo()); return nNewTime - nOldTime; } @@ -105,7 +105,7 @@ void BlockAssembler::resetBlock() nFees = 0; } -std::unique_ptr BlockAssembler::CreateNewBlock(const CScript& scriptPubKeyIn, bool fMineWitnessTx) +std::unique_ptr BlockAssembler::CreateNewBlock(const CScript& scriptPubKeyIn, int algo, bool fMineWitnessTx) { int64_t nTimeStart = GetTimeMicros(); @@ -133,6 +133,9 @@ std::unique_ptr BlockAssembler::CreateNewBlock(const CScript& sc if (chainparams.MineBlocksOnDemand()) pblock->nVersion = gArgs.GetArg("-blockversion", pblock->nVersion); + // multi-algo: encode algo into nVersion + pblock->SetAlgo(algo); + pblock->nTime = GetAdjustedTime(); const int64_t nMedianTimePast = pindexPrev->GetMedianTimePast(); @@ -174,7 +177,7 @@ std::unique_ptr BlockAssembler::CreateNewBlock(const CScript& sc // Fill in header pblock->hashPrevBlock = pindexPrev->GetBlockHash(); UpdateTime(pblock, chainparams.GetConsensus(), pindexPrev); - pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, chainparams.GetConsensus()); + pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, chainparams.GetConsensus(), algo); pblock->nNonce = 0; pblocktemplate->vTxSigOpsCost[0] = WITNESS_SCALE_FACTOR * GetLegacySigOpCount(*pblock->vtx[0]); diff --git a/src/miner.h b/src/miner.h index 9c086332d4..e9e33f5765 100644 --- a/src/miner.h +++ b/src/miner.h @@ -156,7 +156,7 @@ class BlockAssembler BlockAssembler(const CChainParams& params, const Options& options); /** Construct a new block template with coinbase to scriptPubKeyIn */ - std::unique_ptr CreateNewBlock(const CScript& scriptPubKeyIn, bool fMineWitnessTx=true); + std::unique_ptr CreateNewBlock(const CScript& scriptPubKeyIn, int algo, bool fMineWitnessTx=true); private: // utility functions diff --git a/src/pow.cpp b/src/pow.cpp index a30708c39f..1a1fc9a1e8 100644 --- a/src/pow.cpp +++ b/src/pow.cpp @@ -14,7 +14,70 @@ static CBigNum bnProofOfWorkLimit(~arith_uint256(0) >> 20); -unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHeader *pblock, const Consensus::Params& params) +unsigned int GetNextWorkRequired_MultiShield(const CBlockIndex* pindexLast, const Consensus::Params& params, int algo) +{ + const int multiAlgoTargetSpacing = NUM_ALGOS*params.nPowTargetSpacing; //3*150 = 450 seconds per algo + const int nAveragingTargetTimespan = params.nAveragingInterval * multiAlgoTargetSpacing; // 10*3*150 + const int nMinActualTimespan = nAveragingTargetTimespan * (100 - params.nMaxAdjustUp) / 100; + const int nMaxActualTimespan = nAveragingTargetTimespan * (100 + params.nMaxAdjustDown) / 100; + + // find first block in averaging interval + // Go back by what we want to be nAveragingInterval blocks per algo + const CBlockIndex* pindexFirst = pindexLast; + for (int i = 0; pindexFirst && i < NUM_ALGOS*params.nAveragingInterval; i++) + { + pindexFirst = pindexFirst->pprev; + } + + const CBlockIndex* pindexPrevAlgo = GetLastBlockIndexForAlgo(pindexLast, algo); + if (pindexPrevAlgo == nullptr || pindexFirst == nullptr) + { + return UintToArith256(params.powLimit).GetCompact(); + } + + // Limit adjustment step + // Use medians to prevent time-warp attacks + int64_t nActualTimespan = pindexLast-> GetMedianTimePast() - pindexFirst->GetMedianTimePast(); + nActualTimespan = nAveragingTargetTimespan + (nActualTimespan - nAveragingTargetTimespan)/4; + + if (nActualTimespan < nMinActualTimespan) + nActualTimespan = nMinActualTimespan; + if (nActualTimespan > nMaxActualTimespan) + nActualTimespan = nMaxActualTimespan; + + //Global retarget + arith_uint256 bnNew; + bnNew.SetCompact(pindexPrevAlgo->nBits); + + bnNew *= nActualTimespan; + bnNew /= nAveragingTargetTimespan; + + //Per-algo retarget + int nAdjustments = pindexPrevAlgo->nHeight + NUM_ALGOS - 1 - pindexLast->nHeight; + if (nAdjustments > 0) + { + for (int i = 0; i < nAdjustments; i++) + { + bnNew *= 100; + bnNew /= (100 + params.nLocalTargetAdjustment); + } + } + else if (nAdjustments < 0)//make it easier + { + for (int i = 0; i < -nAdjustments; i++) + { + bnNew *= (100 + params.nLocalTargetAdjustment); + bnNew /= 100; + } + } + + if (bnNew > UintToArith256(params.powLimit)) + bnNew = UintToArith256(params.powLimit); + + return bnNew.GetCompact(); +} + +unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHeader *pblock, const Consensus::Params& params, int algo) { static const int64_t BlocksTargetSpacing = 2.5 * 60; // 2.5 minutes unsigned int TimeDaySeconds = 60 * 60 * 24; @@ -26,10 +89,13 @@ unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHead const int nHeight = pindexLast->nHeight + 1; if(params.testnet) { - if(nHeight < 2116) { + if(nHeight < params.nStartKGWWorkCalc) { return GetNextWorkRequired_Bitcoin(pindexLast, pblock, params); } + if(nHeight > params.nStartMultiAlgoHash) + return GetNextWorkRequired_MultiShield(pindexLast, params, algo); + if(nHeight % 12 != 0) { CBigNum bnNew; bnNew.SetCompact(pindexLast->nBits); @@ -37,15 +103,16 @@ unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHead return bnNew.GetCompact(); } } else { - if(nHeight < 26754) { + if(nHeight < params.nStartKGWWorkCalc) { return GetNextWorkRequired_Bitcoin(pindexLast, pblock, params); - } else if(nHeight == 208301) { + } else if(nHeight == params.nStartLyra2reHash) { return 0x1e0ffff0; - } else if(nHeight >= 1080000 && nHeight < 1080010) { // Force difficulty for 10 blocks + } else if(nHeight >= params.nStartLyra2re3Hash && nHeight < (params.nStartLyra2re3Hash + 10)) { // Force difficulty for 10 blocks return 0x1b0ffff0; - } + } else if(nHeight > params.nStartMultiAlgoHash) + return GetNextWorkRequired_MultiShield(pindexLast, params, algo); } - return KimotoGravityWell(pindexLast, pblock, BlocksTargetSpacing, PastBlocksMin, PastBlocksMax, params); + return KimotoGravityWell(pindexLast, pblock, BlocksTargetSpacing, PastBlocksMin, PastBlocksMax, params); } unsigned int GetNextWorkRequired_Bitcoin(const CBlockIndex* pindexLast, const CBlockHeader *pblock, const Consensus::Params& params) @@ -89,10 +156,10 @@ unsigned int GetNextWorkRequired_Bitcoin(const CBlockIndex* pindexLast, const CB return CalculateNextWorkRequired(pindexLast, pindexFirst->GetBlockTime(), params); } -unsigned int KimotoGravityWell(const CBlockIndex* pindexLast, - const CBlockHeader *pblock, - uint64_t TargetBlocksSpacingSeconds, - uint64_t PastBlocksMin, +unsigned int KimotoGravityWell(const CBlockIndex* pindexLast, + const CBlockHeader *pblock, + uint64_t TargetBlocksSpacingSeconds, + uint64_t PastBlocksMin, uint64_t PastBlocksMax, const Consensus::Params& params) { /* current difficulty formula - kimoto gravity well */ @@ -132,11 +199,11 @@ unsigned int KimotoGravityWell(const CBlockIndex* pindexLast, if (PastBlocksMass >= PastBlocksMin) { if ((PastRateAdjustmentRatio <= EventHorizonDeviationSlow) || (PastRateAdjustmentRatio >= EventHorizonDeviationFast)) { assert(BlockReading); break; } } - if (BlockReading->pprev == NULL || + if (BlockReading->pprev == NULL || (!params.testnet && BlockReading->nHeight == 1080000)) // Don't calculate past fork block on mainnet - { - assert(BlockReading); - break; + { + assert(BlockReading); + break; } BlockReading = BlockReading->pprev; } diff --git a/src/pow.h b/src/pow.h index 8f6ccea7b7..cb9a063979 100644 --- a/src/pow.h +++ b/src/pow.h @@ -16,7 +16,7 @@ class uint256; unsigned int GetNextWorkRequired_Bitcoin(const CBlockIndex* pindexLast, const CBlockHeader *pblock, const Consensus::Params&); unsigned int KimotoGravityWell(const CBlockIndex* pindexLast, const CBlockHeader *pblock, uint64_t TargetBlocksSpacingSeconds, uint64_t PastBlocksMin, uint64_t PastBlocksMax, const Consensus::Params& params); -unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHeader *pblock, const Consensus::Params&); +unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHeader *pblock, const Consensus::Params&, int algo); unsigned int CalculateNextWorkRequired(const CBlockIndex* pindexLast, int64_t nFirstBlockTime, const Consensus::Params&); /** Check whether a block hash satisfies the proof-of-work requirement specified by nBits */ diff --git a/src/primitives/block.cpp b/src/primitives/block.cpp index 569b30cc48..257d99f4c2 100644 --- a/src/primitives/block.cpp +++ b/src/primitives/block.cpp @@ -11,23 +11,41 @@ #include #include +#include +#include + uint256 CBlockHeader::GetHash() const { return SerializeHash(*this); } -uint256 CBlockHeader::GetPoWHash(const int nHeight) const +uint256 CBlockHeader::GetPoWHash(const int nHeight, const Consensus::Params& params) const { uint256 thash; - if((Params().NetworkIDString() == CBaseChainParams::TESTNET && nHeight > 158220) || nHeight > 1080000) + if(nHeight > params.nStartMultiAlgoHash) + { + switch (GetAlgo()) + { + case ALGO_LYRA2REV3: + lyra2re3_hash(BEGIN(nVersion), BEGIN(thash)); + break; + case ALGO_NEWALGO1: + lyra2re3_hash(BEGIN(nVersion), BEGIN(thash)); + break; + case ALGO_NEWALGO2: + lyra2re3_hash(BEGIN(nVersion), BEGIN(thash)); + break; + } + } + else if(nHeight > params.nStartLyra2re3Hash) { lyra2re3_hash(BEGIN(nVersion), BEGIN(thash)); } - else if(Params().NetworkIDString() == CBaseChainParams::TESTNET || nHeight >= 347000) // New Lyra2re2 Testnet + else if (nHeight >= params.nStartLyra2re2Hash) { lyra2re2_hash(BEGIN(nVersion), BEGIN(thash)); } - else if(nHeight >= 208301) + else if(nHeight >= params.nStartLyra2reHash) { lyra2re_hash(BEGIN(nVersion), BEGIN(thash)); } @@ -41,9 +59,10 @@ uint256 CBlockHeader::GetPoWHash(const int nHeight) const std::string CBlock::ToString() const { std::stringstream s; - s << strprintf("CBlock(hash=%s, ver=0x%08x, hashPrevBlock=%s, hashMerkleRoot=%s, nTime=%u, nBits=%08x, nNonce=%u, vtx=%u)\n", + s << strprintf("CBlock(hash=%s, ver=0x%08x, pow_algo=%d, hashPrevBlock=%s, hashMerkleRoot=%s, nTime=%u, nBits=%08x, nNonce=%u, vtx=%u)\n", GetHash().ToString(), nVersion, + GetAlgo(), hashPrevBlock.ToString(), hashMerkleRoot.ToString(), nTime, nBits, nNonce, @@ -53,3 +72,44 @@ std::string CBlock::ToString() const } return s.str(); } + +int GetAlgo(int nVersion) +{ + switch (nVersion & BLOCK_VERSION_ALGO) + { + case 0: + return ALGO_LYRA2REV3; + break; + case BLOCK_VERSION_NEWALGO1: + return ALGO_NEWALGO1; + break; + case BLOCK_VERSION_NEWALGO2: + return ALGO_NEWALGO2; + break; + } + return ALGO_LYRA2REV3; +} + +std::string GetAlgoName(int nHeight, int algo, const Consensus::Params& params) +{ + switch (algo) + { + case ALGO_LYRA2REV3: + if(nHeight > params.nStartLyra2re3Hash) + return std::string("lyra2rev3"); + else if (nHeight >= params.nStartLyra2re2Hash) + return std::string("lyra2rev2"); + else if (nHeight >= params.nStartLyra2reHash) + return std::string("lyra2re"); + else + return std::string("scrypt-n"); + break; + case ALGO_NEWALGO1: + return std::string("newalgo1"); + break; + case ALGO_NEWALGO2: + return std::string("newalgo2"); + break; + } + return std::string("unknown"); +} diff --git a/src/primitives/block.h b/src/primitives/block.h index 8701355ded..b9282a5644 100644 --- a/src/primitives/block.h +++ b/src/primitives/block.h @@ -9,10 +9,33 @@ #include #include #include +#include -#include -#include +/** Multi-Algo definitions used to encode algorithm in nVersion */ +enum { + ALGO_LYRA2REV3 = 0, + ALGO_NEWALGO1 = 1, + ALGO_NEWALGO2 = 2, + NUM_ALGOS_IMPL +}; + +const int NUM_ALGOS = 3; + +enum { + // primary version + BLOCK_VERSION_DEFAULT = 4, + // algo + BLOCK_VERSION_ALGO = (15 << 9), + BLOCK_VERSION_NEWALGO1 = (1 << 9), + BLOCK_VERSION_NEWALGO2 = (2 << 9), +}; + +/** extract algo from nVersion */ +int GetAlgo(int nVersion); + +/** return algorithm name */ +std::string GetAlgoName(int nHeight, int algo, const Consensus::Params& params); /** Nodes collect new transactions into a block, hash them into a hash tree, * and scan through nonce values to make the block's hash satisfy proof-of-work @@ -66,12 +89,36 @@ class CBlockHeader uint256 GetHash() const; - uint256 GetPoWHash(const int nHeight) const; + uint256 GetPoWHash(const int nHeight, const Consensus::Params& params) const; int64_t GetBlockTime() const { return (int64_t)nTime; } + + /** Extract algo from blockheader */ + inline int GetAlgo() const + { + return ::GetAlgo(nVersion); + } + + /** Encode the algorithm into nVersion */ + inline void SetAlgo(int algo) + { + switch(algo) + { + case ALGO_LYRA2REV3: + break; + case ALGO_NEWALGO1: + nVersion |= BLOCK_VERSION_NEWALGO1; + break; + case ALGO_NEWALGO2: + nVersion |= BLOCK_VERSION_NEWALGO2; + break; + default: + break; + } + } }; diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 5a3b62f63f..391106c887 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -51,19 +51,30 @@ extern void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& /* Calculate the difficulty for a given block index, * or the block index of the given chain. */ -double GetDifficulty(const CChain& chain, const CBlockIndex* blockindex) +double GetDifficulty(const CChain& chain, const CBlockIndex* blockindex, int algo) { + unsigned int nBits; + unsigned int powLimit = UintToArith256(Params().GetConsensus().powLimit).GetCompact(); + if (blockindex == nullptr) { if (chain.Tip() == nullptr) - return 1.0; + nBits = powLimit; else - blockindex = chain.Tip(); + { + blockindex = GetLastBlockIndexForAlgo(chain.Tip(), algo); + if (blockindex == nullptr) + nBits = powLimit; + else + nBits = blockindex->nBits; + } } + else + nBits = blockindex->nBits; - int nShift = (blockindex->nBits >> 24) & 0xff; + int nShift = (nBits >> 24) & 0xff; double dDiff = - (double)0x0000ffff / (double)(blockindex->nBits & 0x00ffffff); + (double)0x0000ffff / (double)(nBits & 0x00ffffff); while (nShift < 29) { @@ -79,9 +90,9 @@ double GetDifficulty(const CChain& chain, const CBlockIndex* blockindex) return dDiff; } -double GetDifficulty(const CBlockIndex* blockindex) +double GetDifficulty(int algo, const CBlockIndex* blockindex) { - return GetDifficulty(chainActive, blockindex); + return GetDifficulty(chainActive, blockindex, algo); } UniValue blockheaderToJSON(const CBlockIndex* blockindex) @@ -102,7 +113,9 @@ UniValue blockheaderToJSON(const CBlockIndex* blockindex) result.push_back(Pair("mediantime", (int64_t)blockindex->GetMedianTimePast())); result.push_back(Pair("nonce", (uint64_t)blockindex->nNonce)); result.push_back(Pair("bits", strprintf("%08x", blockindex->nBits))); - result.push_back(Pair("difficulty", GetDifficulty(blockindex))); + result.push_back(Pair("pow_algo_id", blockindex->GetAlgo())); + result.push_back(Pair("pow_algo", GetAlgoName(blockindex->nHeight, blockindex->GetAlgo(), Params().GetConsensus()))); + result.push_back(Pair("difficulty", GetDifficulty(blockindex->GetAlgo(), blockindex))); result.push_back(Pair("chainwork", blockindex->nChainWork.GetHex())); if (blockindex->pprev) @@ -147,7 +160,10 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool tx result.push_back(Pair("mediantime", (int64_t)blockindex->GetMedianTimePast())); result.push_back(Pair("nonce", (uint64_t)block.nNonce)); result.push_back(Pair("bits", strprintf("%08x", block.nBits))); - result.push_back(Pair("difficulty", GetDifficulty(blockindex))); + result.push_back(Pair("pow_algo_id", block.GetAlgo())); + result.push_back(Pair("pow_algo", GetAlgoName(blockindex->nHeight, block.GetAlgo(), Params().GetConsensus()))); + result.push_back(Pair("pow_hash", block.GetPoWHash(blockindex->nHeight, Params().GetConsensus()).GetHex())); + result.push_back(Pair("difficulty", GetDifficulty(blockindex->GetAlgo(), blockindex))); result.push_back(Pair("chainwork", blockindex->nChainWork.GetHex())); if (blockindex->pprev) @@ -353,7 +369,7 @@ UniValue getdifficulty(const JSONRPCRequest& request) ); LOCK(cs_main); - return GetDifficulty(); + return GetDifficulty(miningAlgo); } std::string EntryDescriptionString() @@ -676,6 +692,8 @@ UniValue getblockheader(const JSONRPCRequest& request) " \"mediantime\" : ttt, (numeric) The median block time in seconds since epoch (Jan 1 1970 GMT)\n" " \"nonce\" : n, (numeric) The nonce\n" " \"bits\" : \"1d00ffff\", (string) The bits\n" + " \"pow_algo_id\" : n, (numeric) The algorithm id\n" + " \"pow_algo\" : \"name\", (string) The algorithm name\n" " \"difficulty\" : x.xxx, (numeric) The difficulty\n" " \"chainwork\" : \"0000...1f3\" (string) Expected number of hashes required to produce the current chain (in hex)\n" " \"previousblockhash\" : \"hash\", (string) The hash of the previous block\n" @@ -745,6 +763,9 @@ UniValue getblock(const JSONRPCRequest& request) " \"mediantime\" : ttt, (numeric) The median block time in seconds since epoch (Jan 1 1970 GMT)\n" " \"nonce\" : n, (numeric) The nonce\n" " \"bits\" : \"1d00ffff\", (string) The bits\n" + " \"pow_algo_id\" : n, (numeric) The algorithm id\n" + " \"pow_algo\" : \"name\", (string) The algorithm name\n" + " \"pow_hash\" : \"hash\" (string) The proof-of-work hash\n" " \"difficulty\" : x.xxx, (numeric) The difficulty\n" " \"chainwork\" : \"xxxx\", (string) Expected number of hashes required to produce the chain up to this block (in hex)\n" " \"previousblockhash\" : \"hash\", (string) The hash of the previous block\n" @@ -1128,6 +1149,9 @@ UniValue getblockchaininfo(const JSONRPCRequest& request) " \"headers\": xxxxxx, (numeric) the current number of headers we have validated\n" " \"bestblockhash\": \"...\", (string) the hash of the currently best block\n" " \"difficulty\": xxxxxx, (numeric) the current difficulty\n" + " \"difficulty_lyra2rev3\": xxxxxx, (numeric) the current lyra2rev3 difficulty\n" + " \"difficulty_newalgo1\": xxxxxx, (numeric) the current newalgo1 difficulty\n" + " \"difficulty_newalgo2\": xxxxxx, (numeric) the current newalgo2 difficulty\n" " \"mediantime\": xxxxxx, (numeric) median time for the current best block\n" " \"verificationprogress\": xxxx, (numeric) estimate of verification progress [0..1]\n" " \"initialblockdownload\": xxxx, (bool) (debug information) estimate of whether this node is in Initial Block Download mode.\n" @@ -1167,7 +1191,10 @@ UniValue getblockchaininfo(const JSONRPCRequest& request) obj.push_back(Pair("blocks", (int)chainActive.Height())); obj.push_back(Pair("headers", pindexBestHeader ? pindexBestHeader->nHeight : -1)); obj.push_back(Pair("bestblockhash", chainActive.Tip()->GetBlockHash().GetHex())); - obj.push_back(Pair("difficulty", (double)GetDifficulty())); + obj.push_back(Pair("difficulty", (double)GetDifficulty(miningAlgo))); + obj.push_back(Pair("difficulty_lyra2rev3", (double)GetDifficulty(ALGO_LYRA2REV3))); + obj.push_back(Pair("difficulty_newalgo1", (double)GetDifficulty(ALGO_NEWALGO1))); + obj.push_back(Pair("difficulty_newalgo2", (double)GetDifficulty(ALGO_NEWALGO2))); obj.push_back(Pair("mediantime", (int64_t)chainActive.Tip()->GetMedianTimePast())); obj.push_back(Pair("verificationprogress", GuessVerificationProgress(Params().TxData(), chainActive.Tip()))); obj.push_back(Pair("initialblockdownload", IsInitialBlockDownload())); @@ -1517,7 +1544,7 @@ UniValue getchaintxstats(const JSONRPCRequest& request) pindex = chainActive.Tip(); } } - + assert(pindex != nullptr); if (request.params[0].isNull()) { diff --git a/src/rpc/blockchain.h b/src/rpc/blockchain.h index 960edfd56f..d019c747bd 100644 --- a/src/rpc/blockchain.h +++ b/src/rpc/blockchain.h @@ -16,7 +16,7 @@ class UniValue; * @return A floating point number that is a multiple of the main net minimum * difficulty (4295032833 hashes). */ -double GetDifficulty(const CBlockIndex* blockindex = nullptr); +double GetDifficulty(int algo, const CBlockIndex* blockindex = nullptr); /** Callback for when block tip changed. */ void RPCNotifyBlockChange(bool ibd, const CBlockIndex *); diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index caf4098e79..34c637322d 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -118,7 +118,7 @@ UniValue generateBlocks(std::shared_ptr coinbaseScript, int nGen UniValue blockHashes(UniValue::VARR); while (nHeight < nHeightEnd) { - std::unique_ptr pblocktemplate(BlockAssembler(Params()).CreateNewBlock(coinbaseScript->reserveScript)); + std::unique_ptr pblocktemplate(BlockAssembler(Params()).CreateNewBlock(coinbaseScript->reserveScript, miningAlgo)); if (!pblocktemplate.get()) throw JSONRPCError(RPC_INTERNAL_ERROR, "Couldn't create new block"); CBlock *pblock = &pblocktemplate->block; @@ -126,7 +126,7 @@ UniValue generateBlocks(std::shared_ptr coinbaseScript, int nGen LOCK(cs_main); IncrementExtraNonce(pblock, chainActive.Tip(), nExtraNonce); } - while (nMaxTries > 0 && pblock->nNonce < nInnerLoopCount && !CheckProofOfWork(pblock->GetPoWHash(nHeight+1), pblock->nBits, Params().GetConsensus())) { + while (nMaxTries > 0 && pblock->nNonce < nInnerLoopCount && !CheckProofOfWork(pblock->GetPoWHash(nHeight+1, Params().GetConsensus()), pblock->nBits, Params().GetConsensus())) { ++pblock->nNonce; --nMaxTries; } @@ -196,7 +196,12 @@ UniValue getmininginfo(const JSONRPCRequest& request) " \"blocks\": nnn, (numeric) The current block\n" " \"currentblockweight\": nnn, (numeric) The last block weight\n" " \"currentblocktx\": nnn, (numeric) The last block transaction\n" + " \"pow_algo_id\": n (numeric) The active mining algorithm id\n" + " \"pow_algo\": \"name\" (string) The active mining algorithm name\n" " \"difficulty\": xxx.xxxxx (numeric) The current difficulty\n" + " \"difficulty_lyra2rev3\": xxxxxx, (numeric) the current lyra2rev3 difficulty\n" + " \"difficulty_newalgo1\": xxxxxx, (numeric) the current newalgo1 difficulty\n" + " \"difficulty_newalgo2\": xxxxxx, (numeric) the current newalgo2 difficulty\n" " \"networkhashps\": nnn, (numeric) The network hashes per second\n" " \"pooledtx\": n (numeric) The size of the mempool\n" " \"chain\": \"xxxx\", (string) current network name as defined in BIP70 (main, test, regtest)\n" @@ -215,7 +220,12 @@ UniValue getmininginfo(const JSONRPCRequest& request) obj.push_back(Pair("blocks", (int)chainActive.Height())); obj.push_back(Pair("currentblockweight", (uint64_t)nLastBlockWeight)); obj.push_back(Pair("currentblocktx", (uint64_t)nLastBlockTx)); - obj.push_back(Pair("difficulty", (double)GetDifficulty())); + obj.push_back(Pair("pow_algo_id", (int)miningAlgo)); + obj.push_back(Pair("pow_algo", GetAlgoName(chainActive.Height(), miningAlgo, Params().GetConsensus()))); + obj.push_back(Pair("difficulty", (double)GetDifficulty(miningAlgo))); + obj.push_back(Pair("difficulty_lyra2rev3", (double)GetDifficulty(ALGO_LYRA2REV3))); + obj.push_back(Pair("difficulty_newalgo1", (double)GetDifficulty(ALGO_NEWALGO1))); + obj.push_back(Pair("difficulty_newalgo2", (double)GetDifficulty(ALGO_NEWALGO2))); obj.push_back(Pair("networkhashps", getnetworkhashps(request))); obj.push_back(Pair("pooledtx", (uint64_t)mempool.size())); obj.push_back(Pair("chain", Params().NetworkIDString())); @@ -523,7 +533,7 @@ UniValue getblocktemplate(const JSONRPCRequest& request) // Create new block CScript scriptDummy = CScript() << OP_TRUE; - pblocktemplate = BlockAssembler(Params()).CreateNewBlock(scriptDummy, fSupportsSegwit); + pblocktemplate = BlockAssembler(Params()).CreateNewBlock(scriptDummy, miningAlgo, fSupportsSegwit); if (!pblocktemplate) throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory"); diff --git a/src/test/blockchain_tests.cpp b/src/test/blockchain_tests.cpp index 55fdd2c071..474c9182bb 100644 --- a/src/test/blockchain_tests.cpp +++ b/src/test/blockchain_tests.cpp @@ -48,7 +48,7 @@ void TestDifficulty(uint32_t nbits, double expected_difficulty) */ CChain chain; - double difficulty = GetDifficulty(chain, block_index); + double difficulty = GetDifficulty(chain, block_index, ALGO_LYRA2REV3); delete block_index; RejectDifficultyMismatch(difficulty, expected_difficulty); @@ -85,7 +85,7 @@ BOOST_AUTO_TEST_CASE(get_difficulty_for_very_high_target) BOOST_AUTO_TEST_CASE(get_difficulty_for_null_tip) { CChain chain; - double difficulty = GetDifficulty(chain, nullptr); + double difficulty = GetDifficulty(chain, nullptr, ALGO_LYRA2REV3); RejectDifficultyMismatch(difficulty, 1.0); } @@ -96,7 +96,7 @@ BOOST_AUTO_TEST_CASE(get_difficulty_for_null_block_index) { CChain chain = CreateChainWithNbits(0x1df88f6f); - double difficulty = GetDifficulty(chain, nullptr); + double difficulty = GetDifficulty(chain, nullptr, ALGO_LYRA2REV3); delete chain.Tip(); double expected_difficulty = 0.004023; @@ -116,7 +116,7 @@ BOOST_AUTO_TEST_CASE(get_difficulty_for_block_index_overrides_tip) */ CBlockIndex* override_block_index = CreateBlockIndexWithNbits(0x12345678); - double difficulty = GetDifficulty(chain, override_block_index); + double difficulty = GetDifficulty(chain, override_block_index, ALGO_LYRA2REV3); delete chain.Tip(); delete override_block_index; diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp index d9f6772c2d..d32a1639c5 100644 --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -129,7 +129,7 @@ void TestPackageSelection(const CChainParams& chainparams, CScript scriptPubKey, uint256 hashHighFeeTx = tx.GetHash(); mempool.addUnchecked(hashHighFeeTx, entry.Fee(50000).Time(GetTime()).SpendsCoinbase(false).FromTx(tx)); - std::unique_ptr pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey); + std::unique_ptr pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey, ALGO_LYRA2REV3); BOOST_CHECK(pblocktemplate->block.vtx[1]->GetHash() == hashParentTx); BOOST_CHECK(pblocktemplate->block.vtx[2]->GetHash() == hashHighFeeTx); BOOST_CHECK(pblocktemplate->block.vtx[3]->GetHash() == hashMediumFeeTx); @@ -149,7 +149,7 @@ void TestPackageSelection(const CChainParams& chainparams, CScript scriptPubKey, tx.vout[0].nValue = 5000000000LL - 1000 - 50000 - feeToUse; uint256 hashLowFeeTx = tx.GetHash(); mempool.addUnchecked(hashLowFeeTx, entry.Fee(feeToUse).FromTx(tx)); - pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey); + pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey, ALGO_LYRA2REV3); // Verify that the free tx and the low fee tx didn't get selected for (size_t i=0; iblock.vtx.size(); ++i) { BOOST_CHECK(pblocktemplate->block.vtx[i]->GetHash() != hashFreeTx); @@ -163,7 +163,7 @@ void TestPackageSelection(const CChainParams& chainparams, CScript scriptPubKey, tx.vout[0].nValue -= 2; // Now we should be just over the min relay fee hashLowFeeTx = tx.GetHash(); mempool.addUnchecked(hashLowFeeTx, entry.Fee(feeToUse+2).FromTx(tx)); - pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey); + pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey, ALGO_LYRA2REV3); BOOST_CHECK(pblocktemplate->block.vtx[4]->GetHash() == hashFreeTx); BOOST_CHECK(pblocktemplate->block.vtx[5]->GetHash() == hashLowFeeTx); @@ -184,7 +184,7 @@ void TestPackageSelection(const CChainParams& chainparams, CScript scriptPubKey, tx.vout[0].nValue = 5000000000LL - 100000000 - feeToUse; uint256 hashLowFeeTx2 = tx.GetHash(); mempool.addUnchecked(hashLowFeeTx2, entry.Fee(feeToUse).SpendsCoinbase(false).FromTx(tx)); - pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey); + pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey, ALGO_LYRA2REV3); // Verify that this tx isn't selected. for (size_t i=0; iblock.vtx.size(); ++i) { @@ -197,7 +197,7 @@ void TestPackageSelection(const CChainParams& chainparams, CScript scriptPubKey, tx.vin[0].prevout.n = 1; tx.vout[0].nValue = 100000000 - 10000; // 10k satoshi fee mempool.addUnchecked(tx.GetHash(), entry.Fee(10000).FromTx(tx)); - pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey); + pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey, ALGO_LYRA2REV3); BOOST_CHECK(pblocktemplate->block.vtx[8]->GetHash() == hashLowFeeTx2); } @@ -219,7 +219,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) fCheckpointsEnabled = false; // Simple block creation, nothing special yet: - BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey)); + BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey, ALGO_LYRA2REV3)); // We can't make transactions until we have inputs // Therefore, load 100 blocks :) @@ -255,7 +255,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) LOCK(cs_main); // Just to make sure we can still make simple blocks - BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey)); + BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey, ALGO_LYRA2REV3)); const CAmount BLOCKSUBSIDY = 50*COIN; const CAmount LOWFEE = CENT; @@ -280,7 +280,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vin[0].prevout.hash = hash; } - BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error, HasReason("bad-blk-sigops")); + BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey, ALGO_LYRA2REV3), std::runtime_error, HasReason("bad-blk-sigops")); mempool.clear(); tx.vin[0].prevout.hash = txFirst[0]->GetHash(); @@ -294,7 +294,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) mempool.addUnchecked(hash, entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(spendsCoinbase).SigOpsCost(80).FromTx(tx)); tx.vin[0].prevout.hash = hash; } - BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey)); + BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey, ALGO_LYRA2REV3)); mempool.clear(); // block size > limit @@ -314,13 +314,13 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) mempool.addUnchecked(hash, entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(spendsCoinbase).FromTx(tx)); tx.vin[0].prevout.hash = hash; } - BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey)); + BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey, ALGO_LYRA2REV3)); mempool.clear(); // orphan in mempool, template creation fails hash = tx.GetHash(); mempool.addUnchecked(hash, entry.Fee(LOWFEE).Time(GetTime()).FromTx(tx)); - BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error, HasReason("bad-txns-inputs-missingorspent")); + BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey, ALGO_LYRA2REV3), std::runtime_error, HasReason("bad-txns-inputs-missingorspent")); mempool.clear(); // child with higher feerate than parent @@ -337,7 +337,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vout[0].nValue = tx.vout[0].nValue+BLOCKSUBSIDY-HIGHERFEE; //First txn output + fresh coinbase - new txn fee hash = tx.GetHash(); mempool.addUnchecked(hash, entry.Fee(HIGHERFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); - BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey)); + BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey, ALGO_LYRA2REV3)); mempool.clear(); // coinbase in mempool, template creation fails @@ -349,7 +349,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) // give it a fee so it'll get mined mempool.addUnchecked(hash, entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(false).FromTx(tx)); // Should throw bad-cb-multiple - BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error, HasReason("bad-cb-multiple")); + BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey, ALGO_LYRA2REV3), std::runtime_error, HasReason("bad-cb-multiple")); mempool.clear(); // double spend txn pair in mempool, template creation fails @@ -362,7 +362,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vout[0].scriptPubKey = CScript() << OP_2; hash = tx.GetHash(); mempool.addUnchecked(hash, entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); - BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error, HasReason("bad-txns-inputs-missingorspent")); + BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey, ALGO_LYRA2REV3), std::runtime_error, HasReason("bad-txns-inputs-missingorspent")); mempool.clear(); // subsidy changing @@ -378,7 +378,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) next->BuildSkip(); chainActive.SetTip(next); } - BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey)); + BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey, ALGO_LYRA2REV3)); // Extend to a 210000-long block chain. while (chainActive.Tip()->nHeight < 210000) { CBlockIndex* prev = chainActive.Tip(); @@ -390,7 +390,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) next->BuildSkip(); chainActive.SetTip(next); } - BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey)); + BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey, ALGO_LYRA2REV3)); // invalid p2sh txn in mempool, template creation fails tx.vin[0].prevout.hash = txFirst[0]->GetHash(); @@ -407,7 +407,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) hash = tx.GetHash(); mempool.addUnchecked(hash, entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(false).FromTx(tx)); // Should throw block-validation-failed - BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error, HasReason("block-validation-failed")); + BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey, ALGO_LYRA2REV3), std::runtime_error, HasReason("block-validation-failed")); mempool.clear(); // Delete the dummy blocks again. @@ -495,7 +495,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vin[0].nSequence = CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG | 1; BOOST_CHECK(!TestSequenceLocks(tx, flags)); // Sequence locks fail - BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey)); + BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey, ALGO_LYRA2REV3)); // None of the of the absolute height/time locked tx should have made // it into the template because we still check IsFinalTx in CreateNewBlock, @@ -508,7 +508,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) chainActive.Tip()->nHeight++; SetMockTime(chainActive.Tip()->GetMedianTimePast() + 1); - BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey)); + BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey, ALGO_LYRA2REV3)); BOOST_CHECK_EQUAL(pblocktemplate->block.vtx.size(), 5); chainActive.Tip()->nHeight--; diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp index bdd44489f4..4442fffc35 100644 --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -134,7 +134,7 @@ CBlock TestChain100Setup::CreateAndProcessBlock(const std::vector& txns, const CScript& scriptPubKey) { const CChainParams& chainparams = Params(); - std::unique_ptr pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey); + std::unique_ptr pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey, ALGO_LYRA2REV3); CBlock& block = pblocktemplate->block; // Replace mempool-selected txns with just coinbase plus passed-in txns: diff --git a/src/txdb.cpp b/src/txdb.cpp index 1c0829ec23..7ec907a47a 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -54,7 +54,7 @@ struct CoinEntry { } -CCoinsViewDB::CCoinsViewDB(size_t nCacheSize, bool fMemory, bool fWipe) : db(GetDataDir() / "chainstate", nCacheSize, fMemory, fWipe, true) +CCoinsViewDB::CCoinsViewDB(size_t nCacheSize, bool fMemory, bool fWipe) : db(GetDataDir() / "chainstate", nCacheSize, fMemory, fWipe, true) { } @@ -295,7 +295,7 @@ bool CBlockTreeDB::LoadBlockIndexGuts(const Consensus::Params& consensusParams, pindexNew->nTx = diskindex.nTx; MapCheckpoints::iterator it = checkPoints.find(pindexNew->nHeight); - if(it != checkPoints.end()) + if(it != checkPoints.end()) { LogPrintf("Verifying checkpoint at height %i\n", pindexNew->nHeight); if(pindexNew->GetBlockHash() != it->second) @@ -307,7 +307,7 @@ bool CBlockTreeDB::LoadBlockIndexGuts(const Consensus::Params& consensusParams, if(fullChainVerification || pindexNew->nHeight > highestCheckpointHeight) { if(pindexNew->nHeight % 10000 == 0) LogPrintf("Checking PoW for block %i\n", pindexNew->nHeight); - if (!CheckProofOfWork(pindexNew->GetBlockPoWHash(), pindexNew->nBits, consensusParams)) + if (!CheckProofOfWork(pindexNew->GetBlockPoWHash(consensusParams), pindexNew->nBits, consensusParams)) return error("%s: CheckProofOfWork failed: %s\n", __func__, pindexNew->ToString()); } diff --git a/src/util.cpp b/src/util.cpp index f7286de95c..b0f5250654 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -95,6 +95,7 @@ bool fLogTimeMicros = DEFAULT_LOGTIMEMICROS; bool fLogIPs = DEFAULT_LOGIPS; std::atomic fReopenDebugLog(false); CTranslationInterface translationInterface; +int miningAlgo = 0; /** Log categories bitfield. */ std::atomic logCategories(0); diff --git a/src/util.h b/src/util.h index 59baa0b0e4..e8e5f49b25 100644 --- a/src/util.h +++ b/src/util.h @@ -54,6 +54,7 @@ extern bool fLogTimeMicros; extern bool fLogIPs; extern std::atomic fReopenDebugLog; extern CTranslationInterface translationInterface; +extern int miningAlgo; extern const char * const BITCOIN_CONF_FILENAME; extern const char * const BITCOIN_PID_FILENAME; diff --git a/src/validation.cpp b/src/validation.cpp index 30ff1f99d1..73eb1eeefb 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -351,7 +351,7 @@ bool CheckSequenceLocks(const CTransaction &tx, int flags, LockPoints* lp, bool CBlockIndex* tip = chainActive.Tip(); assert(tip != nullptr); - + CBlockIndex index; index.pprev = tip; // CheckSequenceLocks() uses chainActive.Height()+1 to evaluate @@ -1107,7 +1107,7 @@ bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos, const int nHeigh } // Check the header - if (!CheckProofOfWork(block.GetPoWHash(nHeight), block.nBits, consensusParams)) + if (!CheckProofOfWork(block.GetPoWHash(nHeight, consensusParams), block.nBits, consensusParams)) return error("ReadBlockFromDisk: Errors in block header at %s, %d", pos.ToString(), nHeight); return true; @@ -2143,7 +2143,8 @@ void static UpdateTip(const CBlockIndex *pindexNew, const CChainParams& chainPar for (int i = 0; i < 100 && pindex != nullptr; i++) { int32_t nExpectedVersion = ComputeBlockVersion(pindex->pprev, chainParams.GetConsensus()); - if (pindex->nVersion > VERSIONBITS_LAST_OLD_BLOCK_VERSION && (pindex->nVersion & ~nExpectedVersion) != 0) + // multi-algo uses nVersion bits 9 thru 12 for encoding the algorithm. mask this from the version bits check + if (pindex->nVersion > VERSIONBITS_LAST_OLD_BLOCK_VERSION && ((pindex->nVersion & ~nExpectedVersion) & 0xFFFFE1FF) != 0) ++nUpgraded; pindex = pindex->pprev; } @@ -2156,8 +2157,9 @@ void static UpdateTip(const CBlockIndex *pindexNew, const CChainParams& chainPar DoWarning(strWarning); } } - LogPrintf("%s: new best=%s height=%d version=0x%08x log2_work=%.8g tx=%lu date='%s' progress=%f cache=%.1fMiB(%utxo)", __func__, + LogPrintf("%s: new best=%s height=%d version=0x%08x algo=%d (%s) log2_work=%.8g tx=%lu date='%s' progress=%f cache=%.1fMiB(%utxo)", __func__, pindexNew->GetBlockHash().ToString(), pindexNew->nHeight, pindexNew->nVersion, + pindexNew->GetAlgo(), GetAlgoName(pindexNew->nHeight, pindexNew->GetAlgo(), chainParams.GetConsensus()), log(pindexNew->nChainWork.getdouble())/log(2.0), (unsigned long)pindexNew->nChainTx, DateTimeStrFormat("%Y-%m-%d %H:%M:%S", pindexNew->GetBlockTime()), GuessVerificationProgress(chainParams.TxData(), pindexNew), pcoinsTip->DynamicMemoryUsage() * (1.0 / (1<<20)), pcoinsTip->GetCacheSize()); @@ -2938,7 +2940,7 @@ static bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, } // Check proof of work matches claimed amount - if (fCheckPOW && !CheckProofOfWork(block.GetPoWHash(nHeight), block.nBits, consensusParams)) + if (fCheckPOW && !CheckProofOfWork(block.GetPoWHash(nHeight, consensusParams), block.nBits, consensusParams)) return state.DoS(50, false, REJECT_INVALID, "high-hash", false, "proof of work failed"); return true; @@ -3083,9 +3085,18 @@ static bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationSta assert(pindexPrev != nullptr); const int nHeight = pindexPrev->nHeight + 1; - // Check proof of work const Consensus::Params& consensusParams = params.GetConsensus(); - if (block.nBits != GetNextWorkRequired(pindexPrev, &block, consensusParams)) + int algo = block.GetAlgo(); + // Ensure Multi-Algo isn't allowed before nStartMultiAlgoHash + if (nHeight <= consensusParams.nStartMultiAlgoHash && algo!=ALGO_LYRA2REV3) + return state.DoS(100, false, REJECT_INVALID, "early-multi-algo", false, "multi-algo blocks are not allowed at this height"); + + // Ensure a valid algo id has been used + if (algo >= NUM_ALGOS_IMPL) + return state.Invalid(false, REJECT_INVALID, "invalid-algo", "invalid algo id"); + + // Check proof of work + if (block.nBits != GetNextWorkRequired(pindexPrev, &block, consensusParams, algo)) return state.DoS(100, false, REJECT_INVALID, "bad-diffbits", false, "incorrect proof of work"); // Check against checkpoints @@ -3144,7 +3155,7 @@ static bool ContextualCheckBlock(const CBlock& block, CValidationState& state, c } // Enforce rule that the coinbase starts with serialized block height - if(VersionBitsState(pindexPrev, consensusParams, Consensus::DEPLOYMENT_NVERSIONBIPS, versionbitscache) == THRESHOLD_ACTIVE) + if(VersionBitsState(pindexPrev, consensusParams, Consensus::DEPLOYMENT_NVERSIONBIPS, versionbitscache) == THRESHOLD_ACTIVE) { CScript expect = CScript() << nHeight; if (block.vtx[0]->vin[0].scriptSig.size() < expect.size() ||