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 : }