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 139067 : static int64_t GetStakeModifierSelectionIntervalSection(int nSection) 21 : { 22 139067 : assert(nSection >= 0 && nSection < 64); 23 139067 : int64_t a = MODIFIER_INTERVAL * 63 / (63 + ((63 - nSection) * (MODIFIER_INTERVAL_RATIO - 1))); 24 139067 : 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 139067 : 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 139067 : bool fModifierV2 = false; 38 139067 : bool fFirstRun = true; 39 139067 : bool fSelected = false; 40 139067 : arith_uint256 hashBest = ARITH_UINT256_ZERO; 41 139067 : *pindexSelected = (const CBlockIndex*)0; 42 2961439 : for (const auto& item : vSortedByTimestamp) { 43 2957321 : 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 2957321 : const CBlockIndex* pindex = mapBlockIndex[item.second]; 47 2957321 : if (fSelected && pindex->GetBlockTime() > nSelectionIntervalStop) 48 : break; 49 : 50 : //if the lowest block height (vSortedByTimestamp[0]) is >= switch height, use new modifier calc 51 2822371 : if (fFirstRun){ 52 139067 : fModifierV2 = Params().GetConsensus().NetworkUpgradeActive(pindex->nHeight, Consensus::UPGRADE_POS_V2); 53 : fFirstRun = false; 54 : } 55 : 56 2822371 : if (mapSelectedBlocks.count(pindex->GetBlockHash()) > 0) 57 2446505 : continue; 58 : 59 : // compute the selection hash by hashing an input that is unique to that block 60 375863 : uint256 hashProof; 61 375863 : if(fModifierV2) 62 216 : hashProof = pindex->GetBlockHash(); 63 : else 64 375647 : hashProof = pindex->IsProofOfStake() ? UINT256_ZERO : pindex->GetBlockHash(); 65 : 66 751726 : CDataStream ss(SER_GETHASH, 0); 67 375863 : ss << hashProof << nStakeModifierPrev; 68 375863 : 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 375863 : if (pindex->IsProofOfStake()) 74 216 : hashSelection >>= 32; 75 : 76 612659 : if (fSelected && hashSelection < hashBest) { 77 13482 : hashBest = hashSelection; 78 1498 : *pindexSelected = (const CBlockIndex*)pindex; 79 374365 : } else if (!fSelected) { 80 1251602 : fSelected = true; 81 1251602 : hashBest = hashSelection; 82 139067 : *pindexSelected = (const CBlockIndex*)pindex; 83 : } 84 : } 85 139067 : 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 277 : bool GetOldModifier(const CBlockIndex* pindexFrom, uint64_t& nStakeModifier) 91 : { 92 277 : int64_t nStakeModifierTime = pindexFrom->GetBlockTime(); 93 277 : const CBlockIndex* pindex = pindexFrom; 94 277 : CBlockIndex* pindexNext = chainActive[pindex->nHeight + 1]; 95 : 96 : // loop to find the stake modifier later by a selection interval 97 9828 : do { 98 9828 : if (!pindexNext) { 99 : // Should never happen 100 0 : return error("%s : Null pindexNext, current block %s ", __func__, pindex->phashBlock->GetHex()); 101 : } 102 9828 : pindex = pindexNext; 103 9828 : if (pindex->GeneratedStakeModifier()) nStakeModifierTime = pindex->GetBlockTime(); 104 9828 : pindexNext = chainActive[pindex->nHeight + 1]; 105 9828 : } while (nStakeModifierTime < pindexFrom->GetBlockTime() + OLD_MODIFIER_INTERVAL); 106 : 107 277 : nStakeModifier = pindex->GetStakeModifierV1(); 108 277 : return true; 109 : } 110 : 111 277 : bool GetOldStakeModifier(CStakeInput* stake, uint64_t& nStakeModifier) 112 : { 113 277 : const CBlockIndex* pindexFrom = stake->GetIndexFrom(); 114 277 : if (!pindexFrom) return error("%s : failed to get index from", __func__); 115 277 : 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 277 : } 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 1104483 : static bool sortedByTimestamp(const std::pair<uint64_t, uint256>& a, 135 : const std::pair<uint64_t, uint256>& b) 136 : { 137 1104483 : if (a.first == b.first) { 138 199216 : return UintToArith256(a.second) < UintToArith256(b.second); 139 : } 140 1004870 : 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 34346 : bool ComputeNextStakeModifier(const CBlockIndex* pindexPrev, uint64_t& nStakeModifier, bool& fGeneratedStakeModifier) 157 : { 158 34346 : nStakeModifier = 0; 159 34346 : fGeneratedStakeModifier = false; 160 : 161 34346 : if (!pindexPrev) { 162 0 : fGeneratedStakeModifier = true; 163 0 : return true; // genesis block's modifier is 0 164 : } 165 34346 : 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 2049351 : int64_t nModifierTime = 0; 175 : const CBlockIndex* p = pindexPrev; 176 2049351 : while (p && p->pprev && !p->GeneratedStakeModifier()) p = p->pprev; 177 34166 : if (!p->GeneratedStakeModifier()) return error("%s : unable to get last modifier", __func__); 178 34166 : nStakeModifier = p->GetStakeModifierV1(); 179 34166 : nModifierTime = p->GetBlockTime(); 180 : 181 34166 : if (nModifierTime / MODIFIER_INTERVAL >= pindexPrev->GetBlockTime() / MODIFIER_INTERVAL) 182 : return true; 183 : 184 : // Sort candidate blocks by timestamp 185 38501 : std::vector<std::pair<int64_t, uint256> > vSortedByTimestamp; 186 4155 : vSortedByTimestamp.reserve(64 * MODIFIER_INTERVAL / Params().GetConsensus().nTargetSpacing); 187 4155 : int64_t nSelectionIntervalStart = (pindexPrev->GetBlockTime() / MODIFIER_INTERVAL ) * MODIFIER_INTERVAL - OLD_MODIFIER_INTERVAL; 188 4155 : const CBlockIndex* pindex = pindexPrev; 189 : 190 180228 : while (pindex && pindex->GetBlockTime() >= nSelectionIntervalStart) { 191 176073 : vSortedByTimestamp.emplace_back(pindex->GetBlockTime(), pindex->GetBlockHash()); 192 176073 : pindex = pindex->pprev; 193 : } 194 : 195 4155 : std::reverse(vSortedByTimestamp.begin(), vSortedByTimestamp.end()); 196 4155 : std::sort(vSortedByTimestamp.begin(), vSortedByTimestamp.end(), sortedByTimestamp); 197 : 198 : // Select 64 blocks from candidate blocks to generate stake modifier 199 4155 : uint64_t nStakeModifierNew = 0; 200 4155 : int64_t nSelectionIntervalStop = nSelectionIntervalStart; 201 8310 : std::map<uint256, const CBlockIndex*> mapSelectedBlocks; 202 280204 : for (int nRound = 0; nRound < std::min(64, (int)vSortedByTimestamp.size()); nRound++) { 203 : // add an interval section to the current selection round 204 139067 : nSelectionIntervalStop += GetStakeModifierSelectionIntervalSection(nRound); 205 : 206 : // select a block from the candidates of current round 207 139067 : 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 139067 : nStakeModifierNew |= (((uint64_t)pindex->GetStakeEntropyBit()) << nRound); 212 : 213 : // add the selected block from candidates to selected list 214 139067 : mapSelectedBlocks.emplace(pindex->GetBlockHash(), pindex); 215 : } 216 : 217 4155 : nStakeModifier = nStakeModifierNew; 218 4155 : fGeneratedStakeModifier = true; 219 4155 : return true; 220 : }