Stake Modifier (hash modifier of proof-of-stake): The purpose of stake modifier is to prevent a txout (coin) owner from computing future proof-of-stake generated by this txout at the time of transaction confirmation. To meet kernel protocol, the txout must hash with a future stake modifier to generate the proof. Stake modifier consists of bits each of which is contributed from a selected block of
| 232 | // additional bits in the stake modifier, even after generating a chain of |
| 233 | // blocks. |
| 234 | bool Stake::ComputeNextModifier(const CBlockIndex* pindexPrev, uint64_t& nStakeModifier, bool& fGeneratedStakeModifier) { |
| 235 | nStakeModifier = 0; |
| 236 | fGeneratedStakeModifier = false; |
| 237 | |
| 238 | if (!pindexPrev) { |
| 239 | fGeneratedStakeModifier = true; |
| 240 | return true; // genesis block's modifier is 0 |
| 241 | } |
| 242 | |
| 243 | if (pindexPrev->nHeight == 0) { |
| 244 | //Give a stake modifier to the first block |
| 245 | fGeneratedStakeModifier = true; |
| 246 | nStakeModifier = uint64_t("stakemodifier"); |
| 247 | return true; |
| 248 | } |
| 249 | |
| 250 | // First find current stake modifier and its generation block time |
| 251 | // if it's not old enough, return the same stake modifier |
| 252 | int64_t nModifierTime = 0; |
| 253 | if (!GetLastStakeModifier(pindexPrev, nStakeModifier, nModifierTime)) |
| 254 | return error("%s: unable to get last modifier", __func__); |
| 255 | |
| 256 | # if defined(DEBUG_DUMP_STAKING_INFO) && false |
| 257 | if (GetBoolArg("-printstakemodifier", false)) |
| 258 | LogPrintf("%s: last modifier=%d time=%d\n", __func__, nStakeModifier, nModifierTime); // DateTimeStrFormat("%Y-%m-%d %H:%M:%S", nModifierTime) |
| 259 | # endif |
| 260 | |
| 261 | auto const nPrevRounds = pindexPrev->GetBlockTime() / GetInterval(); |
| 262 | if (nModifierTime / GetInterval() >= nPrevRounds) |
| 263 | return true; |
| 264 | |
| 265 | // Sort candidate blocks by timestamp |
| 266 | vector< pair<int64_t, uint256> > vSortedCandidates; |
| 267 | vSortedCandidates.reserve(64 * GetInterval() / GetTargetSpacing(pindexPrev->nHeight)); |
| 268 | |
| 269 | int64_t nSelectionTime = nPrevRounds * GetInterval() - GetSelectionTime(); |
| 270 | const CBlockIndex* pindex = pindexPrev; |
| 271 | while (pindex && pindex->GetBlockTime() >= nSelectionTime) { |
| 272 | if (pindex->IsProofOfStake() && pindex->hashProofOfStake == 0) { |
| 273 | return error("%s: zero stake block %s", __func__, pindex->GetBlockHash().GetHex()); |
| 274 | } |
| 275 | vSortedCandidates.push_back(make_pair(pindex->GetBlockTime(), pindex->GetBlockHash())); |
| 276 | pindex = pindex->pprev; |
| 277 | } |
| 278 | |
| 279 | # if defined(DEBUG_DUMP_STAKING_INFO) && false |
| 280 | int nHeightFirstCandidate = pindex ? (pindex->nHeight + 1) : 0; |
| 281 | # endif |
| 282 | |
| 283 | //reverse(vSortedCandidates.begin(), vSortedCandidates.end()); |
| 284 | sort(vSortedCandidates.begin(), vSortedCandidates.end()); |
| 285 | |
| 286 | // Select 64 blocks from candidate blocks to generate stake modifier |
| 287 | uint64_t nStakeModifierNew = 0; |
| 288 | map<uint256, const CBlockIndex*> mSelectedBlocks; |
| 289 | for (int nRound = 0; nRound < min(64, int(vSortedCandidates.size())); nRound++) { |
| 290 | // add an interval section to the current selection round |
| 291 | nSelectionTime += GetSelectionInterval(nRound); |
no test coverage detected