LCOV - code coverage report
Current view: top level - src/legacy - stakemodifier.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 90 103 87.4 %
Date: 2025-02-23 09:33:43 Functions: 6 6 100.0 %

          Line data    Source code
       1             : // Copyright (c) 2011-2013 The PPCoin developers
       2             : // Copyright (c) 2013-2014 The NovaCoin Developers
       3             : // Copyright (c) 2014-2018 The BlackCoin Developers
       4             : // Copyright (c) 2015-2021 The PIVX Core developers
       5             : // Distributed under the MIT/X11 software license, see the accompanying
       6             : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
       7             : 
       8             : #include "legacy/stakemodifier.h"
       9             : #include "validation.h"   // mapBlockIndex, chainActive
      10             : 
      11             : /*
      12             :  * Old Modifier - Only for IBD
      13             :  */
      14             : 
      15             : static const unsigned int MODIFIER_INTERVAL = 60;
      16             : static const int MODIFIER_INTERVAL_RATIO = 3;
      17             : static const int64_t OLD_MODIFIER_INTERVAL = 2087;
      18             : 
      19             : // Get selection interval section (in seconds)
      20      140658 : static int64_t GetStakeModifierSelectionIntervalSection(int nSection)
      21             : {
      22      140658 :     assert(nSection >= 0 && nSection < 64);
      23      140658 :     int64_t a = MODIFIER_INTERVAL  * 63 / (63 + ((63 - nSection) * (MODIFIER_INTERVAL_RATIO - 1)));
      24      140658 :     return a;
      25             : }
      26             : 
      27             : // select a block from the candidate blocks in vSortedByTimestamp, excluding
      28             : // already selected blocks in vSelectedBlocks, and with timestamp up to
      29             : // nSelectionIntervalStop.
      30      140658 : static bool SelectBlockFromCandidates(
      31             :     std::vector<std::pair<int64_t, uint256> >& vSortedByTimestamp,
      32             :     std::map<uint256, const CBlockIndex*>& mapSelectedBlocks,
      33             :     int64_t nSelectionIntervalStop,
      34             :     uint64_t nStakeModifierPrev,
      35             :     const CBlockIndex** pindexSelected)
      36             : {
      37      140658 :     bool fModifierV2 = false;
      38      140658 :     bool fFirstRun = true;
      39      140658 :     bool fSelected = false;
      40      140658 :     arith_uint256 hashBest = ARITH_UINT256_ZERO;
      41      140658 :     *pindexSelected = (const CBlockIndex*)0;
      42     3333105 :     for (const auto& item : vSortedByTimestamp) {
      43     3328968 :         if (!mapBlockIndex.count(item.second))
      44           0 :             return error("%s : failed to find block index for candidate block %s", __func__, item.second.ToString().c_str());
      45             : 
      46     3328968 :         const CBlockIndex* pindex = mapBlockIndex[item.second];
      47     3328968 :         if (fSelected && pindex->GetBlockTime() > nSelectionIntervalStop)
      48             :             break;
      49             : 
      50             :         //if the lowest block height (vSortedByTimestamp[0]) is >= switch height, use new modifier calc
      51     3192443 :         if (fFirstRun){
      52      140658 :             fModifierV2 = Params().GetConsensus().NetworkUpgradeActive(pindex->nHeight, Consensus::UPGRADE_POS_V2);
      53             :             fFirstRun = false;
      54             :         }
      55             : 
      56     3192443 :         if (mapSelectedBlocks.count(pindex->GetBlockHash()) > 0)
      57     2498071 :             continue;
      58             : 
      59             :         // compute the selection hash by hashing an input that is unique to that block
      60      694377 :         uint256 hashProof;
      61      694377 :         if(fModifierV2)
      62         216 :             hashProof = pindex->GetBlockHash();
      63             :         else
      64      694161 :             hashProof = pindex->IsProofOfStake() ? UINT256_ZERO : pindex->GetBlockHash();
      65             : 
      66     1388754 :         CDataStream ss(SER_GETHASH, 0);
      67      694377 :         ss << hashProof << nStakeModifierPrev;
      68      694377 :         arith_uint256 hashSelection = UintToArith256(Hash(ss.begin(), ss.end()));
      69             : 
      70             :         // the selection hash is divided by 2**32 so that proof-of-stake block
      71             :         // is always favored over proof-of-work block. this is to preserve
      72             :         // the energy efficiency property
      73      694377 :         if (pindex->IsProofOfStake())
      74         216 :             hashSelection >>= 32;
      75             : 
      76     1248092 :         if (fSelected && hashSelection < hashBest) {
      77       21636 :             hashBest = hashSelection;
      78        2404 :             *pindexSelected = (const CBlockIndex*)pindex;
      79      691973 :         } else if (!fSelected) {
      80     1265918 :             fSelected = true;
      81     1265918 :             hashBest = hashSelection;
      82      140658 :             *pindexSelected = (const CBlockIndex*)pindex;
      83             :         }
      84             :     }
      85      140658 :     return fSelected;
      86             : }
      87             : 
      88             : // The stake modifier used to hash for a stake kernel is chosen as the stake
      89             : // modifier about a selection interval later than the coin generating the kernel
      90         244 : bool GetOldModifier(const CBlockIndex* pindexFrom, uint64_t& nStakeModifier)
      91             : {
      92         244 :     int64_t nStakeModifierTime = pindexFrom->GetBlockTime();
      93         244 :     const CBlockIndex* pindex = pindexFrom;
      94         244 :     CBlockIndex* pindexNext = chainActive[pindex->nHeight + 1];
      95             : 
      96             :     // loop to find the stake modifier later by a selection interval
      97        8656 :     do {
      98        8656 :         if (!pindexNext) {
      99             :             // Should never happen
     100           0 :             return error("%s : Null pindexNext, current block %s ", __func__, pindex->phashBlock->GetHex());
     101             :         }
     102        8656 :         pindex = pindexNext;
     103        8656 :         if (pindex->GeneratedStakeModifier()) nStakeModifierTime = pindex->GetBlockTime();
     104        8656 :         pindexNext = chainActive[pindex->nHeight + 1];
     105        8656 :     } while (nStakeModifierTime < pindexFrom->GetBlockTime() + OLD_MODIFIER_INTERVAL);
     106             : 
     107         244 :     nStakeModifier = pindex->GetStakeModifierV1();
     108         244 :     return true;
     109             : }
     110             : 
     111         244 : bool GetOldStakeModifier(CStakeInput* stake, uint64_t& nStakeModifier)
     112             : {
     113         244 :     const CBlockIndex* pindexFrom = stake->GetIndexFrom();
     114         244 :     if (!pindexFrom) return error("%s : failed to get index from", __func__);
     115         244 :     if (stake->IsZPIV()) {
     116           0 :         int64_t nTimeBlockFrom = pindexFrom->GetBlockTime();
     117           0 :         const int nHeightStop = std::min(chainActive.Height(), Params().GetConsensus().height_last_ZC_AccumCheckpoint-1);
     118           0 :         while (pindexFrom && pindexFrom->nHeight + 1 <= nHeightStop) {
     119           0 :             if (pindexFrom->GetBlockTime() - nTimeBlockFrom > 60 * 60) {
     120           0 :                 nStakeModifier = pindexFrom->nAccumulatorCheckpoint.GetCheapHash();
     121           0 :                 return true;
     122             :             }
     123           0 :             pindexFrom = chainActive.Next(pindexFrom);
     124             :         }
     125             :         return false;
     126             : 
     127         244 :     } else if (!GetOldModifier(pindexFrom, nStakeModifier))
     128           0 :         return error("%s : failed to get kernel stake modifier", __func__);
     129             : 
     130             :     return true;
     131             : }
     132             : 
     133             : // sort blocks by timestamp, soliving tie with hash (taken as arith_uint)
     134     1380126 : static bool sortedByTimestamp(const std::pair<uint64_t, uint256>& a,
     135             :                               const std::pair<uint64_t, uint256>& b)
     136             : {
     137     1380126 :     if (a.first == b.first) {
     138      305402 :         return UintToArith256(a.second) < UintToArith256(b.second);
     139             :     }
     140     1227424 :     return a.first < b.first;
     141             : }
     142             : 
     143             : // Stake Modifier (hash modifier of proof-of-stake):
     144             : // The purpose of stake modifier is to prevent a txout (coin) owner from
     145             : // computing future proof-of-stake generated by this txout at the time
     146             : // of transaction confirmation. To meet kernel protocol, the txout
     147             : // must hash with a future stake modifier to generate the proof.
     148             : // Stake modifier consists of bits each of which is contributed from a
     149             : // selected block of a given block group in the past.
     150             : // The selection of a block is based on a hash of the block's proof-hash and
     151             : // the previous stake modifier.
     152             : // Stake modifier is recomputed at a fixed time interval instead of every
     153             : // block. This is to make it difficult for an attacker to gain control of
     154             : // additional bits in the stake modifier, even after generating a chain of
     155             : // blocks.
     156       34522 : bool ComputeNextStakeModifier(const CBlockIndex* pindexPrev, uint64_t& nStakeModifier, bool& fGeneratedStakeModifier)
     157             : {
     158       34522 :     nStakeModifier = 0;
     159       34522 :     fGeneratedStakeModifier = false;
     160             : 
     161       34522 :     if (!pindexPrev) {
     162           0 :         fGeneratedStakeModifier = true;
     163           0 :         return true; // genesis block's modifier is 0
     164             :     }
     165       34522 :     if (pindexPrev->nHeight == 0) {
     166             :         //Give a stake modifier to the first block
     167         180 :         fGeneratedStakeModifier = true;
     168         180 :         nStakeModifier = uint64_t("stakemodifier");
     169         180 :         return true;
     170             :     }
     171             : 
     172             :     // First find current stake modifier and its generation block time
     173             :     // if it's not old enough, return the same stake modifier
     174     1996946 :     int64_t nModifierTime = 0;
     175             :     const CBlockIndex* p = pindexPrev;
     176     1996946 :     while (p && p->pprev && !p->GeneratedStakeModifier()) p = p->pprev;
     177       34342 :     if (!p->GeneratedStakeModifier()) return error("%s : unable to get last modifier", __func__);
     178       34342 :     nStakeModifier = p->GetStakeModifierV1();
     179       34342 :     nModifierTime = p->GetBlockTime();
     180             : 
     181       34342 :     if (nModifierTime / MODIFIER_INTERVAL >= pindexPrev->GetBlockTime() / MODIFIER_INTERVAL)
     182             :         return true;
     183             : 
     184             :     // Sort candidate blocks by timestamp
     185       38700 :     std::vector<std::pair<int64_t, uint256> > vSortedByTimestamp;
     186        4178 :     vSortedByTimestamp.reserve(64 * MODIFIER_INTERVAL  / Params().GetConsensus().nTargetSpacing);
     187        4178 :     int64_t nSelectionIntervalStart = (pindexPrev->GetBlockTime() / MODIFIER_INTERVAL ) * MODIFIER_INTERVAL  - OLD_MODIFIER_INTERVAL;
     188        4178 :     const CBlockIndex* pindex = pindexPrev;
     189             : 
     190      203014 :     while (pindex && pindex->GetBlockTime() >= nSelectionIntervalStart) {
     191      198836 :         vSortedByTimestamp.emplace_back(pindex->GetBlockTime(), pindex->GetBlockHash());
     192      198836 :         pindex = pindex->pprev;
     193             :     }
     194             : 
     195        4178 :     std::reverse(vSortedByTimestamp.begin(), vSortedByTimestamp.end());
     196        4178 :     std::sort(vSortedByTimestamp.begin(), vSortedByTimestamp.end(), sortedByTimestamp);
     197             : 
     198             :     // Select 64 blocks from candidate blocks to generate stake modifier
     199        4178 :     uint64_t nStakeModifierNew = 0;
     200        4178 :     int64_t nSelectionIntervalStop = nSelectionIntervalStart;
     201        8356 :     std::map<uint256, const CBlockIndex*> mapSelectedBlocks;
     202      282002 :     for (int nRound = 0; nRound < std::min(64, (int)vSortedByTimestamp.size()); nRound++) {
     203             :         // add an interval section to the current selection round
     204      140658 :         nSelectionIntervalStop += GetStakeModifierSelectionIntervalSection(nRound);
     205             : 
     206             :         // select a block from the candidates of current round
     207      140658 :         if (!SelectBlockFromCandidates(vSortedByTimestamp, mapSelectedBlocks, nSelectionIntervalStop, nStakeModifier, &pindex))
     208           0 :             return error("%s : unable to select block at round %d", __func__, nRound);
     209             : 
     210             :         // write the entropy bit of the selected block
     211      140658 :         nStakeModifierNew |= (((uint64_t)pindex->GetStakeEntropyBit()) << nRound);
     212             : 
     213             :         // add the selected block from candidates to selected list
     214      140658 :         mapSelectedBlocks.emplace(pindex->GetBlockHash(), pindex);
     215             :     }
     216             : 
     217        4178 :     nStakeModifier = nStakeModifierNew;
     218        4178 :     fGeneratedStakeModifier = true;
     219        4178 :     return true;
     220             : }

Generated by: LCOV version 1.14