PIVX Core  5.6.99
P2P Digital Currency
stakemodifier.cpp
Go to the documentation of this file.
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 static int64_t GetStakeModifierSelectionIntervalSection(int nSection)
21 {
22  assert(nSection >= 0 && nSection < 64);
23  int64_t a = MODIFIER_INTERVAL * 63 / (63 + ((63 - nSection) * (MODIFIER_INTERVAL_RATIO - 1)));
24  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 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  bool fModifierV2 = false;
38  bool fFirstRun = true;
39  bool fSelected = false;
41  *pindexSelected = (const CBlockIndex*)0;
42  for (const auto& item : vSortedByTimestamp) {
43  if (!mapBlockIndex.count(item.second))
44  return error("%s : failed to find block index for candidate block %s", __func__, item.second.ToString().c_str());
45 
46  const CBlockIndex* pindex = mapBlockIndex[item.second];
47  if (fSelected && pindex->GetBlockTime() > nSelectionIntervalStop)
48  break;
49 
50  //if the lowest block height (vSortedByTimestamp[0]) is >= switch height, use new modifier calc
51  if (fFirstRun){
53  fFirstRun = false;
54  }
55 
56  if (mapSelectedBlocks.count(pindex->GetBlockHash()) > 0)
57  continue;
58 
59  // compute the selection hash by hashing an input that is unique to that block
60  uint256 hashProof;
61  if(fModifierV2)
62  hashProof = pindex->GetBlockHash();
63  else
64  hashProof = pindex->IsProofOfStake() ? UINT256_ZERO : pindex->GetBlockHash();
65 
66  CDataStream ss(SER_GETHASH, 0);
67  ss << hashProof << nStakeModifierPrev;
68  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  if (pindex->IsProofOfStake())
74  hashSelection >>= 32;
75 
76  if (fSelected && hashSelection < hashBest) {
77  hashBest = hashSelection;
78  *pindexSelected = (const CBlockIndex*)pindex;
79  } else if (!fSelected) {
80  fSelected = true;
81  hashBest = hashSelection;
82  *pindexSelected = (const CBlockIndex*)pindex;
83  }
84  }
85  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 bool GetOldModifier(const CBlockIndex* pindexFrom, uint64_t& nStakeModifier)
91 {
92  int64_t nStakeModifierTime = pindexFrom->GetBlockTime();
93  const CBlockIndex* pindex = pindexFrom;
94  CBlockIndex* pindexNext = chainActive[pindex->nHeight + 1];
95 
96  // loop to find the stake modifier later by a selection interval
97  do {
98  if (!pindexNext) {
99  // Should never happen
100  return error("%s : Null pindexNext, current block %s ", __func__, pindex->phashBlock->GetHex());
101  }
102  pindex = pindexNext;
103  if (pindex->GeneratedStakeModifier()) nStakeModifierTime = pindex->GetBlockTime();
104  pindexNext = chainActive[pindex->nHeight + 1];
105  } while (nStakeModifierTime < pindexFrom->GetBlockTime() + OLD_MODIFIER_INTERVAL);
106 
107  nStakeModifier = pindex->GetStakeModifierV1();
108  return true;
109 }
110 
111 bool GetOldStakeModifier(CStakeInput* stake, uint64_t& nStakeModifier)
112 {
113  const CBlockIndex* pindexFrom = stake->GetIndexFrom();
114  if (!pindexFrom) return error("%s : failed to get index from", __func__);
115  if (stake->IsZPIV()) {
116  int64_t nTimeBlockFrom = pindexFrom->GetBlockTime();
117  const int nHeightStop = std::min(chainActive.Height(), Params().GetConsensus().height_last_ZC_AccumCheckpoint-1);
118  while (pindexFrom && pindexFrom->nHeight + 1 <= nHeightStop) {
119  if (pindexFrom->GetBlockTime() - nTimeBlockFrom > 60 * 60) {
120  nStakeModifier = pindexFrom->nAccumulatorCheckpoint.GetCheapHash();
121  return true;
122  }
123  pindexFrom = chainActive.Next(pindexFrom);
124  }
125  return false;
126 
127  } else if (!GetOldModifier(pindexFrom, nStakeModifier))
128  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 static bool sortedByTimestamp(const std::pair<uint64_t, uint256>& a,
135  const std::pair<uint64_t, uint256>& b)
136 {
137  if (a.first == b.first) {
138  return UintToArith256(a.second) < UintToArith256(b.second);
139  }
140  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 bool ComputeNextStakeModifier(const CBlockIndex* pindexPrev, uint64_t& nStakeModifier, bool& fGeneratedStakeModifier)
157 {
158  nStakeModifier = 0;
159  fGeneratedStakeModifier = false;
160 
161  if (!pindexPrev) {
162  fGeneratedStakeModifier = true;
163  return true; // genesis block's modifier is 0
164  }
165  if (pindexPrev->nHeight == 0) {
166  //Give a stake modifier to the first block
167  fGeneratedStakeModifier = true;
168  nStakeModifier = uint64_t("stakemodifier");
169  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  int64_t nModifierTime = 0;
175  const CBlockIndex* p = pindexPrev;
176  while (p && p->pprev && !p->GeneratedStakeModifier()) p = p->pprev;
177  if (!p->GeneratedStakeModifier()) return error("%s : unable to get last modifier", __func__);
178  nStakeModifier = p->GetStakeModifierV1();
179  nModifierTime = p->GetBlockTime();
180 
181  if (nModifierTime / MODIFIER_INTERVAL >= pindexPrev->GetBlockTime() / MODIFIER_INTERVAL)
182  return true;
183 
184  // Sort candidate blocks by timestamp
185  std::vector<std::pair<int64_t, uint256> > vSortedByTimestamp;
186  vSortedByTimestamp.reserve(64 * MODIFIER_INTERVAL / Params().GetConsensus().nTargetSpacing);
187  int64_t nSelectionIntervalStart = (pindexPrev->GetBlockTime() / MODIFIER_INTERVAL ) * MODIFIER_INTERVAL - OLD_MODIFIER_INTERVAL;
188  const CBlockIndex* pindex = pindexPrev;
189 
190  while (pindex && pindex->GetBlockTime() >= nSelectionIntervalStart) {
191  vSortedByTimestamp.emplace_back(pindex->GetBlockTime(), pindex->GetBlockHash());
192  pindex = pindex->pprev;
193  }
194 
195  std::reverse(vSortedByTimestamp.begin(), vSortedByTimestamp.end());
196  std::sort(vSortedByTimestamp.begin(), vSortedByTimestamp.end(), sortedByTimestamp);
197 
198  // Select 64 blocks from candidate blocks to generate stake modifier
199  uint64_t nStakeModifierNew = 0;
200  int64_t nSelectionIntervalStop = nSelectionIntervalStart;
201  std::map<uint256, const CBlockIndex*> mapSelectedBlocks;
202  for (int nRound = 0; nRound < std::min(64, (int)vSortedByTimestamp.size()); nRound++) {
203  // add an interval section to the current selection round
204  nSelectionIntervalStop += GetStakeModifierSelectionIntervalSection(nRound);
205 
206  // select a block from the candidates of current round
207  if (!SelectBlockFromCandidates(vSortedByTimestamp, mapSelectedBlocks, nSelectionIntervalStop, nStakeModifier, &pindex))
208  return error("%s : unable to select block at round %d", __func__, nRound);
209 
210  // write the entropy bit of the selected block
211  nStakeModifierNew |= (((uint64_t)pindex->GetStakeEntropyBit()) << nRound);
212 
213  // add the selected block from candidates to selected list
214  mapSelectedBlocks.emplace(pindex->GetBlockHash(), pindex);
215  }
216 
217  nStakeModifier = nStakeModifierNew;
218  fGeneratedStakeModifier = true;
219  return true;
220 }
arith_uint256 UintToArith256(const uint256 &a)
const arith_uint256 ARITH_UINT256_ZERO
const CChainParams & Params()
Return the currently selected parameters.
The block chain is a tree shaped structure starting with the genesis block at the root,...
Definition: chain.h:139
CBlockIndex * pprev
pointer to the index of the predecessor of this block
Definition: chain.h:145
unsigned int GetStakeEntropyBit() const
Definition: chain.cpp:218
bool IsProofOfStake() const
Definition: chain.h:224
bool GeneratedStakeModifier() const
Definition: chain.h:231
uint256 nAccumulatorCheckpoint
Definition: chain.h:199
uint256 GetBlockHash() const
Definition: chain.h:215
int64_t GetBlockTime() const
Definition: chain.h:216
int nHeight
height of the entry in the chain. The genesis block has height 0
Definition: chain.h:151
const uint256 * phashBlock
pointer to the hash of the block, if any. memory is owned by this CBlockIndex
Definition: chain.h:142
uint64_t GetStakeModifierV1() const
Definition: chain.cpp:279
CBlockIndex * Next(const CBlockIndex *pindex) const
Find the successor of a block in this chain, or nullptr if the given index is not found or is the tip...
Definition: chain.h:441
int Height() const
Return the maximal height in the chain.
Definition: chain.h:450
const Consensus::Params & GetConsensus() const
Definition: chainparams.h:72
virtual const CBlockIndex * GetIndexFrom() const =0
virtual bool IsZPIV() const =0
256-bit unsigned big integer.
std::string GetHex() const
Definition: uint256.cpp:21
256-bit opaque blob.
Definition: uint256.h:138
uint64_t GetCheapHash() const
A cheap hash function that just returns 64 bits from the result, it can be used when the contents are...
Definition: uint256.h:149
uint256 Hash(const T1 pbegin, const T1 pend)
Compute the 256-bit hash of an object.
Definition: hash.h:173
@ UPGRADE_POS_V2
Definition: params.h:29
@ SER_GETHASH
Definition: serialize.h:176
bool GetOldModifier(const CBlockIndex *pindexFrom, uint64_t &nStakeModifier)
bool GetOldStakeModifier(CStakeInput *stake, uint64_t &nStakeModifier)
bool ComputeNextStakeModifier(const CBlockIndex *pindexPrev, uint64_t &nStakeModifier, bool &fGeneratedStakeModifier)
bool NetworkUpgradeActive(int nHeight, Consensus::UpgradeIndex idx) const
Returns true if the given network upgrade is active as of the given block height.
Definition: params.cpp:12
bool error(const char *fmt, const Args &... args)
Definition: system.h:77
const uint256 UINT256_ZERO
constant uint256 instances
Definition: uint256.h:175
BlockMap mapBlockIndex
Definition: validation.cpp:82
CChain chainActive
The currently-connected chain of blocks (protected by cs_main).
Definition: validation.cpp:84