diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 77013e2082..9f90262641 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -186,6 +186,13 @@ class CMainParams : public CChainParams { consensus.stage2ZnodeShare = 35; consensus.stage2DevelopmentFundAddress = "aFrAVZFr8pva5mG8XKaUH8EXcFVVNxLiuB"; + consensus.stage3StartTime = 1655380800; // Thursday, 16 June 2022 12:00:00 UTC + consensus.stage3DevelopmentFundShare = 15; + consensus.stage3CommunityFundShare = 10; + consensus.stage3MasternodeShare = 50; + consensus.stage3DevelopmentFundAddress = "aLgRaYSFk6iVw2FqY1oei8Tdn2aTsGPVmP"; + consensus.stage3CommunityFundAddress = "aFA2TbqG9cnhhzX5Yny2pBJRK5EaEqLCH7"; + consensus.nStartBlacklist = 293990; consensus.nStartDuplicationCheck = 293526; @@ -445,6 +452,7 @@ class CMainParams : public CChainParams { // ProgPow consensus.nPPSwitchTime = 1635228000; // Tue Oct 26 2021 06:00:00 GMT+0000 + consensus.nPPBlockNumber = 419264; consensus.nInitialPPDifficulty = 0x1b1774cd; // 40GH/s } virtual bool SkipUndoForBlock(int nHeight) const @@ -482,13 +490,20 @@ class CTestNetParams : public CChainParams { consensus.chainType = Consensus::chainTestnet; consensus.nSubsidyHalvingFirst = 12000; - consensus.nSubsidyHalvingInterval = 100000; + consensus.nSubsidyHalvingInterval = 150000; consensus.nSubsidyHalvingStopBlock = 1000000; consensus.stage2DevelopmentFundShare = 15; consensus.stage2ZnodeShare = 35; consensus.stage2DevelopmentFundAddress = "TUuKypsbbnHHmZ2auC2BBWfaP1oTEnxjK2"; + consensus.stage3StartTime = 1653409800; // May 24th 2022 04:30 UTC + consensus.stage3DevelopmentFundShare = 15; + consensus.stage3CommunityFundShare = 10; + consensus.stage3MasternodeShare = 50; + consensus.stage3DevelopmentFundAddress = "TWDxLLKsFp6qcV1LL4U2uNmW4HwMcapmMU"; + consensus.stage3CommunityFundAddress = "TCkC4uoErEyCB4MK3d6ouyJELoXnuyqe9L"; + consensus.nStartBlacklist = 0; consensus.nStartDuplicationCheck = 0; consensus.nMajorityEnforceBlockUpgrade = 51; @@ -720,6 +735,7 @@ class CTestNetParams : public CChainParams { // ProgPow consensus.nPPSwitchTime = 1630069200; // August 27 2021, 13:00 UTC + consensus.nPPBlockNumber = 37305; consensus.nInitialPPDifficulty = 0x1d016e81; // 10MH/s } }; @@ -744,6 +760,13 @@ class CDevNetParams : public CChainParams { consensus.stage2ZnodeShare = 35; consensus.stage2DevelopmentFundAddress = "TixHByoJ21dmx5xfMAXTVC4V7k53U7RncU"; + consensus.stage3StartTime = 1653382800; + consensus.stage3DevelopmentFundShare = 15; + consensus.stage3CommunityFundShare = 10; + consensus.stage3MasternodeShare = 50; + consensus.stage3DevelopmentFundAddress = "TepVKkmUo1N6sazuM2wWwV7aiG4m1BUShU"; + consensus.stage3CommunityFundAddress = "TZpbhfvQE61USHsxd55XdPpWBqu3SXB1EP"; + consensus.nStartBlacklist = 0; consensus.nStartDuplicationCheck = 0; consensus.nMajorityEnforceBlockUpgrade = 51; @@ -937,6 +960,7 @@ class CDevNetParams : public CChainParams { // ProgPow consensus.nPPSwitchTime = 1631261566; // immediately after network start + consensus.nPPBlockNumber = 1; consensus.nInitialPPDifficulty = 0x2000ffff; } }; @@ -954,15 +978,22 @@ class CRegTestParams : public CChainParams { consensus.chainType = Consensus::chainRegtest; // To be changed for specific tests - consensus.nSubsidyHalvingFirst = 302438; - consensus.nSubsidyHalvingInterval = 420000; - consensus.nSubsidyHalvingStopBlock = 3646849; + consensus.nSubsidyHalvingFirst = 1500; + consensus.nSubsidyHalvingInterval = 1000; + consensus.nSubsidyHalvingStopBlock = 10000; consensus.nStartBlacklist = 0; consensus.nStartDuplicationCheck = 0; consensus.stage2DevelopmentFundShare = 15; consensus.stage2ZnodeShare = 35; + consensus.stage3StartTime = INT_MAX; // Thursday, 16 June 2022 12:00:00 UTC + consensus.stage3DevelopmentFundShare = 15; + consensus.stage3CommunityFundShare = 10; + consensus.stage3MasternodeShare = 50; + consensus.stage3DevelopmentFundAddress = "TGEGf26GwyUBE2P2o2beBAfE9Y438dCp5t"; // private key cMrz8Df36VR9TvZjtvSqLPhUQR7pcpkXRXaLNYUxfkKsRuCzHpAN + consensus.stage3CommunityFundAddress = "TJmPzeJF4DECrBwUftc265U7rTPxKmpa4F"; // private key cTyPWqTMM1CgT5qy3K3LSgC1H6Q2RHvnXZHvjWtKB4vq9qXqKmMu + consensus.nMajorityEnforceBlockUpgrade = 750; consensus.nMajorityRejectBlockOutdated = 950; consensus.nMajorityWindow = 1000; @@ -1155,6 +1186,7 @@ class CRegTestParams : public CChainParams { // ProgPow // this can be overridden with either -ppswitchtime or -ppswitchtimefromnow flags consensus.nPPSwitchTime = INT_MAX; + consensus.nPPBlockNumber = INT_MAX; consensus.nInitialPPDifficulty = 0x2000ffff; } diff --git a/src/consensus/params.h b/src/consensus/params.h index 1dc7ba4e9b..58f3a0fd36 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -137,7 +137,7 @@ struct Params { /** Stop subsidy at this block number */ int nSubsidyHalvingStopBlock; - /** parameters for coinbase payment distribution between first and second halvings (aka stage 2) */ + /** parameters for coinbase payment distribution between first halving and stage 3 (aka stage 2) */ /** P2PKH or P2SH address for developer funds */ std::string stage2DevelopmentFundAddress; /** percentage of block subsidy going to developer fund */ @@ -145,6 +145,20 @@ struct Params { /** percentage of block subsidy going to znode */ int stage2ZnodeShare; + /** parameters for coinbase payment distribution after stage two and before second halving (aka stage 3) */ + /** start time of stage 3 */ + int stage3StartTime; + /** P2PKH or P2SH address for developer funds */ + std::string stage3DevelopmentFundAddress; + /** P2PKH or P2SH address for community funds */ + std::string stage3CommunityFundAddress; + /** percentage of block subsidy going to developer fund */ + int stage3DevelopmentFundShare; + /** percentage of block subsidy going to community fund */ + int stage3CommunityFundShare; + /** percentage of block subsidy going to masternode */ + int stage3MasternodeShare; + int nStartDuplicationCheck; int nStartBlacklist; @@ -314,6 +328,8 @@ struct Params { uint32_t nPPSwitchTime; /** initial difficulty for ProgPOW */ int nInitialPPDifficulty; + /** block height at the moment of PP transition (0 if unknown) */ + int nPPBlockNumber; /** don't adjust difficulty until some block number */ int nDifficultyAdjustStartBlock; diff --git a/src/init.cpp b/src/init.cpp index 99267fc18b..ae9fa59906 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1688,7 +1688,7 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) nMaxOutboundLimit = GetArg("-maxuploadtarget", DEFAULT_MAX_UPLOAD_TARGET)*1024*1024; } - // ********************************************************* Prepare ProgPow/MTP tests + // ********************************************************* Prepare ProgPow/MTP/Stage3 tests Consensus::Params &mutableParams = const_cast(Params().GetConsensus()); if (Params().GetConsensus().IsRegtest()) { @@ -1701,6 +1701,11 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) mutableParams.nMTPSwitchTime = GetArg("-mtpswitchtime", INT_MAX); else if (IsArgSet("-mtpswitchtimefromnow")) mutableParams.nMTPSwitchTime = GetArg("-mtpswitchtimefromnow", 0) + (uint32_t)GetTime(); + + else if (IsArgSet("-stage3switchtime")) + mutableParams.stage3StartTime = GetArg("-stage3switchtime", INT_MAX); + else if (IsArgSet("-stage3switchtimefromnow")) + mutableParams.stage3StartTime = GetArg("-stage3switchtimefromnow", 0) + (uint32_t)GetTime(); } if (IsArgSet("-mtpstripdatatime")) diff --git a/src/masternode-payments.cpp b/src/masternode-payments.cpp index c53673f57d..907a303072 100644 --- a/src/masternode-payments.cpp +++ b/src/masternode-payments.cpp @@ -87,7 +87,7 @@ bool IsBlockValueValid(const CBlock& block, int nBlockHeight, CAmount blockRewar return isBlockRewardValueMet; } -bool IsBlockPayeeValid(const CTransaction& txNew, int nBlockHeight, CAmount blockReward) +bool IsBlockPayeeValid(const CTransaction& txNew, int nBlockHeight, int nTime, CAmount blockReward) { if(fLiteMode) { //there is no budget data to use to check anything, let's just accept the longest chain @@ -101,7 +101,7 @@ bool IsBlockPayeeValid(const CTransaction& txNew, int nBlockHeight, CAmount bloc const Consensus::Params& consensusParams = Params().GetConsensus(); // Check for correct masternode payment - if(mnpayments.IsTransactionValid(txNew, nBlockHeight, blockReward)) { + if(mnpayments.IsTransactionValid(txNew, nBlockHeight, nTime, blockReward)) { LogPrint("mnpayments", "%s -- Valid znode payment at height %d: %s", __func__, nBlockHeight, txNew.ToString()); return true; } @@ -110,9 +110,9 @@ bool IsBlockPayeeValid(const CTransaction& txNew, int nBlockHeight, CAmount bloc return false; } -void FillBlockPayments(CMutableTransaction& txNew, int nBlockHeight, CAmount blockReward, std::vector& voutMasternodePaymentsRet, std::vector& /*voutSuperblockPaymentsRet*/) +void FillBlockPayments(CMutableTransaction& txNew, int nBlockHeight, int nTime, CAmount blockReward, std::vector& voutMasternodePaymentsRet, std::vector& /*voutSuperblockPaymentsRet*/) { - if (!mnpayments.GetMasternodeTxOuts(nBlockHeight, blockReward, voutMasternodePaymentsRet)) { + if (!mnpayments.GetMasternodeTxOuts(nBlockHeight, nTime, blockReward, voutMasternodePaymentsRet)) { LogPrint("mnpayments", "%s -- no znode to pay (MN list probably empty)\n", __func__); } @@ -188,12 +188,12 @@ std::map GetRequiredPaymentsStrings(int nStartHeight, int nEnd * Get masternode payment tx outputs */ -bool CMasternodePayments::GetMasternodeTxOuts(int nBlockHeight, CAmount blockReward, std::vector& voutMasternodePaymentsRet) const +bool CMasternodePayments::GetMasternodeTxOuts(int nBlockHeight, int nTime, CAmount blockReward, std::vector& voutMasternodePaymentsRet) const { // make sure it's not filled yet voutMasternodePaymentsRet.clear(); - if(!GetBlockTxOuts(nBlockHeight, blockReward, voutMasternodePaymentsRet)) { + if(!GetBlockTxOuts(nBlockHeight, nTime, blockReward, voutMasternodePaymentsRet)) { LogPrintf("CMasternodePayments::%s -- no payee (deterministic znode list empty)\n", __func__); return false; } @@ -209,14 +209,14 @@ bool CMasternodePayments::GetMasternodeTxOuts(int nBlockHeight, CAmount blockRew return true; } -bool CMasternodePayments::GetBlockTxOuts(int nBlockHeight, CAmount blockReward, std::vector& voutMasternodePaymentsRet) const +bool CMasternodePayments::GetBlockTxOuts(int nBlockHeight, int nTime, CAmount blockReward, std::vector& voutMasternodePaymentsRet) const { voutMasternodePaymentsRet.clear(); if (nBlockHeight == 0) { return false; } - CAmount masternodeReward = GetMasternodePayment(nBlockHeight, blockReward); + CAmount masternodeReward = GetMasternodePayment(nBlockHeight, nTime, blockReward); const CBlockIndex* pindex; { @@ -260,7 +260,7 @@ bool CMasternodePayments::IsScheduled(const CDeterministicMNCPtr& dmnIn, int nNo return false; } -bool CMasternodePayments::IsTransactionValid(const CTransaction& txNew, int nBlockHeight, CAmount blockReward) const +bool CMasternodePayments::IsTransactionValid(const CTransaction& txNew, int nBlockHeight, int nTime, CAmount blockReward) const { if (!deterministicMNManager->IsDIP3Enforced(nBlockHeight)) { // can't verify historical blocks here @@ -268,7 +268,7 @@ bool CMasternodePayments::IsTransactionValid(const CTransaction& txNew, int nBlo } std::vector voutMasternodePayments; - if (!GetBlockTxOuts(nBlockHeight, blockReward, voutMasternodePayments)) { + if (!GetBlockTxOuts(nBlockHeight, nTime, blockReward, voutMasternodePayments)) { LogPrintf("CMasternodePayments::%s -- ERROR failed to get payees for block at height %s\n", __func__, nBlockHeight); return true; } diff --git a/src/masternode-payments.h b/src/masternode-payments.h index db6a7fd010..6c6ab9d9db 100644 --- a/src/masternode-payments.h +++ b/src/masternode-payments.h @@ -17,8 +17,8 @@ class CMasternodePayments; /// TODO: all 4 functions do not belong here really, they should be refactored/moved somewhere (main.cpp ?) bool IsBlockValueValid(const CBlock& block, int nBlockHeight, CAmount blockReward, std::string& strErrorRet); -bool IsBlockPayeeValid(const CTransaction& txNew, int nBlockHeight, CAmount blockReward); -void FillBlockPayments(CMutableTransaction& txNew, int nBlockHeight, CAmount blockReward, std::vector& voutMasternodePaymentsRet, std::vector& voutSuperblockPaymentsRet); +bool IsBlockPayeeValid(const CTransaction& txNew, int nBlockHeight, int nTime, CAmount blockReward); +void FillBlockPayments(CMutableTransaction& txNew, int nBlockHeight, int nTime, CAmount blockReward, std::vector& voutMasternodePaymentsRet, std::vector& voutSuperblockPaymentsRet); std::map GetRequiredPaymentsStrings(int nStartHeight, int nEndHeight); extern CMasternodePayments mnpayments; @@ -31,11 +31,11 @@ extern CMasternodePayments mnpayments; class CMasternodePayments { public: - bool GetBlockTxOuts(int nBlockHeight, CAmount blockReward, std::vector& voutMasternodePaymentsRet) const; - bool IsTransactionValid(const CTransaction& txNew, int nBlockHeight, CAmount blockReward) const; + bool GetBlockTxOuts(int nBlockHeight, int nTime, CAmount blockReward, std::vector& voutMasternodePaymentsRet) const; + bool IsTransactionValid(const CTransaction& txNew, int nBlockHeight, int nTime, CAmount blockReward) const; bool IsScheduled(const CDeterministicMNCPtr& dmn, int nNotBlockHeight) const; - bool GetMasternodeTxOuts(int nBlockHeight, CAmount blockReward, std::vector& voutMasternodePaymentsRet) const; + bool GetMasternodeTxOuts(int nBlockHeight, int nTime, CAmount blockReward, std::vector& voutMasternodePaymentsRet) const; }; #endif diff --git a/src/miner.cpp b/src/miner.cpp index 8c5067dcc4..c514d649cc 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -187,6 +187,7 @@ std::unique_ptr BlockAssembler::CreateNewBlock(const CScript& sc pblock->nTime = GetAdjustedTime(); bool fMTP = (pblock->nTime >= params.nMTPSwitchTime); + bool fShorterBlockDistance = (pblock->nTime >= params.stage3StartTime); const int64_t nMedianTimePast = pindexPrev->GetMedianTimePast(); pblock->nVersion = ComputeBlockVersion(pindexPrev, chainparams.GetConsensus()) | (fMTP ? 0x1000 : 0); @@ -248,7 +249,7 @@ std::unique_ptr BlockAssembler::CreateNewBlock(const CScript& sc coinbaseTx.vout[0].nValue = nFees + nBlockSubsidy; coinbaseTx.vin[0].scriptSig = CScript() << nHeight << OP_0; - FillFoundersReward(coinbaseTx, fMTP); + FillFoundersReward(coinbaseTx, fMTP, fShorterBlockDistance); if (fDIP0003Active_context) { coinbaseTx.vin[0].scriptSig = CScript() << OP_RETURN; @@ -281,7 +282,7 @@ std::unique_ptr BlockAssembler::CreateNewBlock(const CScript& sc } std::vector sbPayments; - FillBlockPayments(coinbaseTx, nHeight, nBlockSubsidy, pblocktemplate->voutMasternodePayments, sbPayments); + FillBlockPayments(coinbaseTx, nHeight, pblock->nTime, nBlockSubsidy, pblocktemplate->voutMasternodePayments, sbPayments); pblock->vtx[0] = MakeTransactionRef(std::move(coinbaseTx)); pblocktemplate->vTxFees[0] = -nFees; @@ -800,17 +801,34 @@ void BlockAssembler::addPriorityTxs() fNeedSizeAccounting = fSizeAccounting; } -void BlockAssembler::FillFoundersReward(CMutableTransaction &coinbaseTx, bool fMTP) { +void BlockAssembler::FillFoundersReward(CMutableTransaction &coinbaseTx, bool fMTP, bool fShorterBlockDistance) { const auto ¶ms = chainparams.GetConsensus(); CAmount coin = COIN / (fMTP ? params.nMTPRewardReduction : 1); + if (fShorterBlockDistance) + coin /= 2; if (nHeight >= params.nSubsidyHalvingFirst && nHeight < params.nSubsidyHalvingFirst + params.nSubsidyHalvingInterval) { - // Stage 2 - CScript devPayoutScript = GetScriptForDestination(CBitcoinAddress(params.stage2DevelopmentFundAddress).Get()); - CAmount devPayoutValue = (GetBlockSubsidyWithMTPFlag(nHeight, params, fMTP) * params.stage2DevelopmentFundShare) / 100; + if (fShorterBlockDistance) { + // Stage 3 + CScript devPayoutScript = GetScriptForDestination(CBitcoinAddress(params.stage3DevelopmentFundAddress).Get()); + CAmount devPayoutValue = (GetBlockSubsidyWithMTPFlag(nHeight, params, fMTP, true) * params.stage3DevelopmentFundShare) / 100; + CScript communityPayoutScript = GetScriptForDestination(CBitcoinAddress(params.stage3CommunityFundAddress).Get()); + CAmount communityPayoutValue = (GetBlockSubsidyWithMTPFlag(nHeight, params, fMTP, true) * params.stage3CommunityFundShare) / 100; + + coinbaseTx.vout[0].nValue -= devPayoutValue; + coinbaseTx.vout.push_back(CTxOut(devPayoutValue, devPayoutScript)); + + coinbaseTx.vout[0].nValue -= communityPayoutValue; + coinbaseTx.vout.push_back(CTxOut(communityPayoutValue, communityPayoutScript)); + } + else { + // Stage 2 + CScript devPayoutScript = GetScriptForDestination(CBitcoinAddress(params.stage2DevelopmentFundAddress).Get()); + CAmount devPayoutValue = (GetBlockSubsidyWithMTPFlag(nHeight, params, fMTP, false) * params.stage2DevelopmentFundShare) / 100; - coinbaseTx.vout[0].nValue -= devPayoutValue; - coinbaseTx.vout.push_back(CTxOut(devPayoutValue, devPayoutScript)); + coinbaseTx.vout[0].nValue -= devPayoutValue; + coinbaseTx.vout.push_back(CTxOut(devPayoutValue, devPayoutScript)); + } } else if ((nHeight > 0) && (nHeight < params.nSubsidyHalvingFirst)) { diff --git a/src/miner.h b/src/miner.h index 115522fd25..aec48edb0c 100644 --- a/src/miner.h +++ b/src/miner.h @@ -224,7 +224,7 @@ class BlockAssembler int UpdatePackagesForAdded(const CTxMemPool::setEntries& alreadyAdded, indexed_modified_transaction_set &mapModifiedTx); /** Firo: fill in founders' reward and znode payout outputs */ - void FillFoundersReward(CMutableTransaction &coinbaseTx, bool fMTP); + void FillFoundersReward(CMutableTransaction &coinbaseTx, bool fMTP, bool fShorterBlockDistance); /** Fill txBlackList set */ void FillBlackListForBlockTemplate(); diff --git a/src/pow.cpp b/src/pow.cpp index 5718dfc9f6..32fd9a8b2e 100644 --- a/src/pow.cpp +++ b/src/pow.cpp @@ -81,9 +81,12 @@ unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHead return params.nInitialMTPDifficulty; } - const uint32_t BlocksTargetSpacing = + uint32_t BlocksTargetSpacing = (params.nMTPFiveMinutesStartBlock == 0 && fMTP) || (params.nMTPFiveMinutesStartBlock > 0 && pindexLast->nHeight >= params.nMTPFiveMinutesStartBlock) ? params.nPowTargetSpacingMTP : params.nPowTargetSpacing; + if (pindexLast->nTime >= params.stage3StartTime) + BlocksTargetSpacing /= 2; + unsigned int TimeDaySeconds = 60 * 60 * 24; int64_t PastSecondsMin = TimeDaySeconds * 0.25; // 21600 int64_t PastSecondsMax = TimeDaySeconds * 7;// 604800 @@ -91,24 +94,62 @@ unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHead uint32_t PastBlocksMax = PastSecondsMax / BlocksTargetSpacing; // 1008 blocks uint32_t StartingPoWBlock = 0; - if (pblock->IsProgPow()) { - if (pblock->nTime < params.nPPSwitchTime + BlocksTargetSpacing*PastBlocksMax*3) { - // transition to progpow happened recently, look for the first PP block + if (pblock->IsShorterBlocksSpacing()) { + if (pindexLast->nTime < params.stage3StartTime) { + // first time we see a block with shorter interval + // Normally we should take difficulty and halve it. But to give some leeway to the miners + // we divide it by 4 + uint32_t base = (pindexLast->nBits & 0x007fffff) << 2; + uint32_t exponent = pindexLast->nBits >> 24; + + if (base > 0x007fffff) { + base >>= 8; + exponent++; + } + return (exponent << 24) | base; + } + + if (pblock->nTime < params.stage3StartTime + BlocksTargetSpacing*PastBlocksMax*3) { + // transition to stage3 happened recently, look for the last block before the transition const CBlockIndex *pindex = pindexLast; - while (pindex && pindex->nTime >= params.nPPSwitchTime) + while (pindex && pindex->nTime >= params.stage3StartTime) pindex = pindex->pprev; if (pindex) { - uint32_t numberOfPPBlocks = pindexLast->nHeight - pindex->nHeight; - if (numberOfPPBlocks < params.DifficultyAdjustmentInterval(true)/2) - // do not retarget if too few PP blocks - return params.nInitialPPDifficulty; + uint32_t numberOfStage3Blocks = pindexLast->nHeight - pindex->nHeight; + if (numberOfStage3Blocks < params.DifficultyAdjustmentInterval(true)/2) + // do not retarget if too few stage3 blocks + return pindexLast->nBits; - PastBlocksMin = std::min(PastBlocksMin, numberOfPPBlocks); - PastBlocksMax = std::min(PastBlocksMax, numberOfPPBlocks); + PastBlocksMin = std::min(PastBlocksMin, numberOfStage3Blocks); + PastBlocksMax = std::min(PastBlocksMax, numberOfStage3Blocks); } } } + else if (pblock->IsProgPow()) { + uint32_t numberOfPPBlocks = 0; + + if (params.nPPBlockNumber != 0) { + // we know the first ProgPow block + numberOfPPBlocks = pindexLast->nHeight - params.nPPBlockNumber + 1; + } + else if (pblock->nTime < params.nPPSwitchTime + BlocksTargetSpacing*PastBlocksMax*3) { + // transition to progpow happened recently, look for the first PP block + const CBlockIndex *pindex = pindexLast; + while (pindex && pindex->nTime >= params.nPPSwitchTime) + pindex = pindex->pprev; + + if (pindex) + numberOfPPBlocks = pindexLast->nHeight - pindex->nHeight; + } + + if (numberOfPPBlocks < params.DifficultyAdjustmentInterval(true)/2) + // do not retarget if too few PP blocks + return params.nInitialPPDifficulty; + + PastBlocksMin = std::min(PastBlocksMin, numberOfPPBlocks); + PastBlocksMax = std::min(PastBlocksMax, numberOfPPBlocks); + } else if (nFirstMTPBlock > 1) { // There are both legacy and MTP blocks in the chain. Limit PoW calculation scope to MTP blocks only uint32_t numberOfMTPBlocks = pindexLast->nHeight - nFirstMTPBlock + 1; diff --git a/src/primitives/block.cpp b/src/primitives/block.cpp index 86eea6d156..e5389b93f8 100644 --- a/src/primitives/block.cpp +++ b/src/primitives/block.cpp @@ -69,6 +69,23 @@ bool CBlockHeader::IsProgPow() const { return (nTime > ZC_GENESIS_BLOCK_TIME && nTime >= Params().GetConsensus().nPPSwitchTime); } +bool CBlockHeader::IsShorterBlocksSpacing() const { + return (nTime > ZC_GENESIS_BLOCK_TIME && nTime >= Params().GetConsensus().stage3StartTime); +} + +int CBlockHeader::GetTargetBlocksSpacing() const { + const Consensus::Params ¶ms = Params().GetConsensus(); + if (nTime <= ZC_GENESIS_BLOCK_TIME) + return params.nPowTargetSpacing; + else if (nTime >= params.stage3StartTime) + return params.nPowTargetSpacingMTP/2; + else if ((params.nMTPFiveMinutesStartBlock == 0 && nTime >= params.nMTPSwitchTime) || + (params.nMTPFiveMinutesStartBlock != 0 && nHeight >= params.nMTPFiveMinutesStartBlock)) + return params.nPowTargetSpacingMTP; + else + return params.nPowTargetSpacing; +} + CProgPowHeader CBlockHeader::GetProgPowHeader() const { return CProgPowHeader { nVersion, diff --git a/src/primitives/block.h b/src/primitives/block.h index 3fb07ed026..a54adda409 100644 --- a/src/primitives/block.h +++ b/src/primitives/block.h @@ -261,8 +261,10 @@ class CBlockHeader void InvalidateCachedPoWHash(int nHeight) const; bool IsMTP() const; - bool IsProgPow() const; + bool IsShorterBlocksSpacing() const; + + int GetTargetBlocksSpacing() const; CProgPowHeader GetProgPowHeader() const; uint256 GetProgPowHeaderHash() const; diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 26b8c297f9..637a079cfb 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -829,7 +829,7 @@ UniValue getblocktemplate(const JSONRPCRequest& request) // Get expected MN/superblock payees. The call to GetBlockTxOuts might fail on regtest/devnet or when // testnet is reset. This is fine and we ignore failure (blocks will be accepted) std::vector voutMasternodePayments; - mnpayments.GetBlockTxOuts(chainActive.Height() + 1, 0, voutMasternodePayments); + mnpayments.GetBlockTxOuts(chainActive.Height() + 1, pblock->nTime, 0, voutMasternodePayments); UniValue masternodeObj(UniValue::VARR); for (const auto& txout : pblocktemplate->voutMasternodePayments) { diff --git a/src/validation.cpp b/src/validation.cpp index 08c85b67fb..d72de51e3c 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -1712,7 +1712,7 @@ bool ReadBlockHeaderFromDisk(CBlock &block, const CDiskBlockPos &pos) { return true; } -CAmount GetBlockSubsidyWithMTPFlag(int nHeight, const Consensus::Params &consensusParams, bool fMTP) { +CAmount GetBlockSubsidyWithMTPFlag(int nHeight, const Consensus::Params &consensusParams, bool fMTP, bool fShorterBlockDistance) { // Genesis block is 0 coin if (nHeight == 0) return 0; @@ -1732,17 +1732,24 @@ CAmount GetBlockSubsidyWithMTPFlag(int nHeight, const Consensus::Params &consens if (nHeight > 0 && fMTP) nSubsidy /= consensusParams.nMTPRewardReduction; + if (nHeight > 0 && fShorterBlockDistance) + nSubsidy /= 2; + return nSubsidy; } CAmount GetBlockSubsidy(int nHeight, const Consensus::Params &consensusParams, int nTime) { - return GetBlockSubsidyWithMTPFlag(nHeight, consensusParams, nTime >= (int)consensusParams.nMTPSwitchTime); + return GetBlockSubsidyWithMTPFlag(nHeight, consensusParams, + nTime >= (int)consensusParams.nMTPSwitchTime, + nTime >= (int)consensusParams.stage3StartTime); } -CAmount GetMasternodePayment(int nHeight, CAmount blockValue) +CAmount GetMasternodePayment(int nHeight, int nTime, CAmount blockValue) { const Consensus::Params ¶ms = Params().GetConsensus(); - if (nHeight >= params.nSubsidyHalvingFirst) + if (nTime >= params.stage3StartTime) + return blockValue*params.stage3MasternodeShare/100; + else if (nHeight >= params.nSubsidyHalvingFirst) return blockValue*params.stage2ZnodeShare/100; else return blockValue*3/10; // 30% @@ -2867,7 +2874,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin return state.DoS(0, error("ConnectBlock(EVOZNODES): %s", strError), REJECT_INVALID, "bad-cb-amount"); } - if (!IsBlockPayeeValid(*block.vtx[0], pindex->nHeight, blockSubsidy)) { + if (!IsBlockPayeeValid(*block.vtx[0], pindex->nHeight, pindex->nTime, blockSubsidy)) { mapRejectedBlocks.insert(std::make_pair(block.GetHash(), GetTime())); return state.DoS(0, error("ConnectBlock(EVPZNODES): couldn't find evo znode payments"), REJECT_INVALID, "bad-cb-payee"); @@ -4539,6 +4546,9 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, const Co if (pindexPrev && pindexPrev->nTime >= consensusParams.nPPSwitchTime && block.nTime < consensusParams.nPPSwitchTime) return state.Invalid(false, REJECT_INVALID, "bad-blk-progpow-state", "Cannot go back from ProgPOW"); + if (pindexPrev && pindexPrev->nTime >= consensusParams.stage3StartTime && block.nTime < consensusParams.stage3StartTime) + return state.Invalid(false, REJECT_INVALID, "bad-blk-stage3-state", "Cannot go back to 5 minutes between blocks"); + if (block.IsProgPow() && block.nHeight != nHeight) return state.DoS(100, false, REJECT_INVALID, "bad-blk-progpow", "ProgPOW height doesn't match chain height"); @@ -4567,16 +4577,34 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, const Co if (nHeight >= consensusParams.nSubsidyHalvingFirst) { if (nHeight < consensusParams.nSubsidyHalvingFirst + consensusParams.nSubsidyHalvingInterval) { - // "stage 2" interval between first and second halvings - CScript devPayoutScript = GetScriptForDestination(CBitcoinAddress(consensusParams.stage2DevelopmentFundAddress).Get()); - CAmount devPayoutValue = (GetBlockSubsidy(nHeight, consensusParams, block.nTime) * consensusParams.stage2DevelopmentFundShare) / 100; - bool found = false; - for (const CTxOut &txout: block.vtx[0]->vout) { - if ((found = txout.scriptPubKey == devPayoutScript && txout.nValue == devPayoutValue) == true) - break; + if (block.nTime >= consensusParams.stage3StartTime) { + CScript devPayoutScript = GetScriptForDestination(CBitcoinAddress(consensusParams.stage3DevelopmentFundAddress).Get()); + CAmount devPayoutValue = (GetBlockSubsidy(nHeight, consensusParams, block.nTime) * consensusParams.stage3DevelopmentFundShare) / 100; + CScript communityPayoutScript = GetScriptForDestination(CBitcoinAddress(consensusParams.stage3CommunityFundAddress).Get()); + CAmount communityPayoutValue = (GetBlockSubsidy(nHeight, consensusParams, block.nTime) * consensusParams.stage3CommunityFundShare) / 100; + + bool devFound = false, communityFound = false; + for (const CTxOut &txout: block.vtx[0]->vout) { + if (txout.scriptPubKey == devPayoutScript && txout.nValue == devPayoutValue) + devFound = true; + else if (txout.scriptPubKey == communityPayoutScript && txout.nValue == communityPayoutValue) + communityFound = true; + } + if (!devFound || !communityFound) + return state.Invalid(false, state.GetRejectCode(), state.GetRejectReason(), "Stage 3 developer/community reward check failed"); + } + else { + // "stage 2" interval between first and second halvings + CScript devPayoutScript = GetScriptForDestination(CBitcoinAddress(consensusParams.stage2DevelopmentFundAddress).Get()); + CAmount devPayoutValue = (GetBlockSubsidy(nHeight, consensusParams, block.nTime) * consensusParams.stage2DevelopmentFundShare) / 100; + bool found = false; + for (const CTxOut &txout: block.vtx[0]->vout) { + if ((found = txout.scriptPubKey == devPayoutScript && txout.nValue == devPayoutValue) == true) + break; + } + if (!found) + return state.Invalid(false, state.GetRejectCode(), state.GetRejectReason(), "Stage 2 developer reward check failed"); } - if (!found) - return state.Invalid(false, state.GetRejectCode(), state.GetRejectReason(), "Stage 2 developer reward check failed"); } } else if (!CheckZerocoinFoundersInputs(*block.vtx[0], state, consensusParams, nHeight, block.IsMTP())) { diff --git a/src/validation.h b/src/validation.h index 85da4f0274..e8e8be17fa 100644 --- a/src/validation.h +++ b/src/validation.h @@ -311,9 +311,9 @@ std::string GetWarnings(const std::string& strFor); bool GetTransaction(const uint256 &hash, CTransactionRef &tx, const Consensus::Params& params, uint256 &hashBlock, bool fAllowSlow = false); /** Find the best known block, and make it the tip of the block chain */ bool ActivateBestChain(CValidationState& state, const CChainParams& chainparams, std::shared_ptr pblock = std::shared_ptr()); -CAmount GetBlockSubsidyWithMTPFlag(int nHeight, const Consensus::Params& consensusParams, bool fMTP); +CAmount GetBlockSubsidyWithMTPFlag(int nHeight, const Consensus::Params& consensusParams, bool fMTP, bool fShorterBlockDistance); CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams, int nTime = 1475020800); -CAmount GetMasternodePayment(int nHeight, CAmount blockValue); +CAmount GetMasternodePayment(int nHeight, int nTime, CAmount blockValue); /** Guess verification progress (as a fraction between 0.0=genesis and 1.0=current tip). */ double GuessVerificationProgress(const ChainTxData& data, CBlockIndex* pindex); diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 60fa9dacdd..a669f17f43 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -1613,7 +1613,7 @@ void ListTransactions(CWallet * const pwallet, const CWalletTx& wtx, const std:: bool its_znode_payment = false; if (!fSkipMnpayoutCheck) { std::vector voutMasternodePaymentsRet; - mnpayments.GetBlockTxOuts(txHeight, CAmount(), voutMasternodePaymentsRet); + mnpayments.GetBlockTxOuts(txHeight, GetTime(), CAmount(), voutMasternodePaymentsRet); //compare address of payee to addr. for(CTxOut const & out : voutMasternodePaymentsRet) { CTxDestination payeeDest;