PIVX Core  5.6.99
P2P Digital Currency
budgetmanager.cpp
Go to the documentation of this file.
1 // Copyright (c) 2014-2015 The Dash developers
2 // Copyright (c) 2015-2022 The PIVX Core developers
3 // Distributed under the MIT/X11 software license, see the accompanying
4 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 
6 #include "budget/budgetmanager.h"
7 
8 #include "consensus/validation.h"
9 #include "evo/deterministicmns.h"
10 #include "masternodeman.h"
11 #include "netmessagemaker.h"
14 #include "util/validation.h"
15 #include "validation.h" // GetTransaction, cs_main
16 
17 #ifdef ENABLE_WALLET
18 #include "wallet/wallet.h" // future: use interface instead.
19 #endif
20 
21 
22 #define BUDGET_ORPHAN_VOTES_CLEANUP_SECONDS (60 * 60) // One hour.
23 // Request type used in the net requests manager to block peers asking budget sync too often
24 static const std::string BUDGET_SYNC_REQUEST_RECV = "budget-sync-recv";
25 
27 
28 // Used to check both proposals and finalized-budgets collateral txes
29 bool CheckCollateral(const uint256& nTxCollateralHash, const uint256& nExpectedHash, std::string& strError, int64_t& nTime, int nCurrentHeight, bool fBudgetFinalization);
30 
32 {
33  const auto reloadSeenMap = [](auto& mutex1, auto& mutex2, const auto& mapBudgets, auto& mapSeen, auto& mapOrphans) {
34  LOCK2(mutex1, mutex2);
35  mapSeen.clear();
36  mapOrphans.clear();
37  for (const auto& b : mapBudgets) {
38  for (const auto& it : b.second.mapVotes) {
39  const auto& vote = it.second;
40  if (vote.IsValid()) {
41  mapSeen.emplace(vote.GetHash(), vote);
42  }
43  }
44  }
45  };
46 
49 }
50 
52 {
53  {
55  for (auto itOrphanVotes = mapOrphanProposalVotes.begin(); itOrphanVotes != mapOrphanProposalVotes.end();) {
56  auto itProposal = mapProposals.find(itOrphanVotes->first);
57  if (itProposal != mapProposals.end()) {
58  // Proposal found.
59  CBudgetProposal* bp = &(itProposal->second);
60  // Try to add orphan votes
61  for (const CBudgetVote& vote : itOrphanVotes->second.first) {
62  std::string strError;
63  if (!bp->AddOrUpdateVote(vote, strError)) {
64  LogPrint(BCLog::MNBUDGET, "Unable to add orphan vote for proposal: %s\n", strError);
65  }
66  }
67  // Remove entry from the map
68  itOrphanVotes = mapOrphanProposalVotes.erase(itOrphanVotes);
69  } else {
70  ++itOrphanVotes;
71  }
72  }
73  }
74 
75  {
77  for (auto itOrphanVotes = mapOrphanFinalizedBudgetVotes.begin(); itOrphanVotes != mapOrphanFinalizedBudgetVotes.end();) {
78  auto itFinalBudget = mapFinalizedBudgets.find(itOrphanVotes->first);
79  if (itFinalBudget != mapFinalizedBudgets.end()) {
80  // Finalized budget found.
81  CFinalizedBudget* fb = &(itFinalBudget->second);
82  // Try to add orphan votes
83  for (const CFinalizedBudgetVote& vote : itOrphanVotes->second.first) {
84  std::string strError;
85  if (!fb->AddOrUpdateVote(vote, strError)) {
86  LogPrint(BCLog::MNBUDGET, "Unable to add orphan vote for final budget: %s\n", strError);
87  }
88  }
89  // Remove entry from the map
90  itOrphanVotes = mapOrphanFinalizedBudgetVotes.erase(itOrphanVotes);
91  } else {
92  ++itOrphanVotes;
93  }
94  }
95  }
96 
97  LogPrint(BCLog::MNBUDGET,"%s: Done\n", __func__);
98 }
99 
101 {
102  static int nSubmittedHeight = 0; // height at which final budget was submitted last time
103  int nCurrentHeight = GetBestHeight();
104 
105  const int nBlocksPerCycle = Params().GetConsensus().nBudgetCycleBlocks;
106  int nBlockStart = nCurrentHeight - nCurrentHeight % nBlocksPerCycle + nBlocksPerCycle;
107  if (nSubmittedHeight >= nBlockStart){
108  LogPrint(BCLog::MNBUDGET,"%s: nSubmittedHeight(=%ld) < nBlockStart(=%ld) condition not fulfilled.\n",
109  __func__, nSubmittedHeight, nBlockStart);
110  return UINT256_ZERO;
111  }
112 
113  // Submit final budget during the last 2 days (2880 blocks) before payment for Mainnet, about 9 minutes (9 blocks) for Testnet
114  int finalizationWindow = ((nBlocksPerCycle / 30) * 2);
115 
116  if (Params().IsTestnet()) {
117  // NOTE: 9 blocks for testnet is way to short to have any masternode submit an automatic vote on the finalized(!) budget,
118  // because those votes are only submitted/relayed once every 56 blocks in CFinalizedBudget::AutoCheck()
119 
120  finalizationWindow = 64; // 56 + 4 finalization confirmations + 4 minutes buffer for propagation
121  }
122 
123  int nFinalizationStart = nBlockStart - finalizationWindow;
124 
125  int nOffsetToStart = nFinalizationStart - nCurrentHeight;
126 
127  if (nBlockStart - nCurrentHeight > finalizationWindow) {
128  LogPrint(BCLog::MNBUDGET,"%s: Too early for finalization. Current block is %ld, next Superblock is %ld.\n", __func__, nCurrentHeight, nBlockStart);
129  LogPrint(BCLog::MNBUDGET,"%s: First possible block for finalization: %ld. Last possible block for finalization: %ld. " /* Continued */
130  "You have to wait for %ld block(s) until Budget finalization will be possible\n", __func__, nFinalizationStart, nBlockStart, nOffsetToStart);
131  return UINT256_ZERO;
132  }
133 
134  std::vector<CBudgetProposal> vBudgetProposals = GetBudget();
135  std::string strBudgetName = "main";
136  std::vector<CTxBudgetPayment> vecTxBudgetPayments;
137 
138  for (const auto& p : vBudgetProposals) {
139  CTxBudgetPayment txBudgetPayment;
140  txBudgetPayment.nProposalHash = p.GetHash();
141  txBudgetPayment.payee = p.GetPayee();
142  txBudgetPayment.nAmount = p.GetAllotted();
143  vecTxBudgetPayments.push_back(txBudgetPayment);
144  }
145 
146  if (vecTxBudgetPayments.size() < 1) {
147  LogPrint(BCLog::MNBUDGET,"%s: Found No Proposals For Period\n", __func__);
148  return UINT256_ZERO;
149  }
150 
151  CFinalizedBudget tempBudget(strBudgetName, nBlockStart, vecTxBudgetPayments, UINT256_ZERO);
152  const uint256& budgetHash = tempBudget.GetHash();
153  if (HaveFinalizedBudget(budgetHash)) {
154  LogPrint(BCLog::MNBUDGET,"%s: Budget already exists - %s\n", __func__, budgetHash.ToString());
155  nSubmittedHeight = nCurrentHeight;
156  return UINT256_ZERO;
157  }
158 
159  // See if collateral tx exists
160  if (!mapUnconfirmedFeeTx.count(budgetHash)) {
161  // create the collateral tx, send it to the network and return
162  CTransactionRef wtx;
163  // Get our change address
164  if (vpwallets.empty() || !vpwallets[0]) {
165  LogPrint(BCLog::MNBUDGET,"%s: Wallet not found\n", __func__);
166  return UINT256_ZERO;
167  }
168  // Exit if wallet is locked
169  if (vpwallets[0]->IsLocked()) {
170  LogPrint(BCLog::MNBUDGET, "%s: Wallet is locked, can't make collateral transaction.\n", __func__);
171  return UINT256_ZERO;
172  }
173  CReserveKey keyChange(vpwallets[0]);
174  if (!vpwallets[0]->CreateBudgetFeeTX(wtx, budgetHash, keyChange, BUDGET_FEE_TX)) {
175  LogPrint(BCLog::MNBUDGET,"%s: Can't make collateral transaction\n", __func__);
176  return UINT256_ZERO;
177  }
178  // Send the tx to the network
179  const CWallet::CommitResult& res = vpwallets[0]->CommitTransaction(wtx, keyChange, g_connman.get());
180  if (res.status == CWallet::CommitStatus::OK) {
181  const uint256& collateraltxid = wtx->GetHash();
182  mapUnconfirmedFeeTx.emplace(budgetHash, collateraltxid);
183  LogPrint(BCLog::MNBUDGET,"%s: Collateral sent. txid: %s\n", __func__, collateraltxid.ToString());
184  return budgetHash;
185  }
186  return UINT256_ZERO;
187  }
188 
189  // Collateral tx already exists, see if it's mature enough.
190  CFinalizedBudget fb(strBudgetName, nBlockStart, vecTxBudgetPayments, mapUnconfirmedFeeTx.at(budgetHash));
191  if (!AddFinalizedBudget(fb)) {
192  return UINT256_ZERO;
193  }
194  fb.Relay();
195  nSubmittedHeight = nCurrentHeight;
196  LogPrint(BCLog::MNBUDGET,"%s: Done! %s\n", __func__, budgetHash.ToString());
197  return budgetHash;
198 }
199 
201 {
202  const std::vector<uint256>& vHashes = finalizedBudget.GetProposalsHashes();
203  std::string strProposals = "";
204  {
206  for (const uint256& hash: vHashes) {
207  const std::string token = (mapProposals.count(hash) ? mapProposals.at(hash).GetName() : hash.ToString());
208  strProposals += (strProposals == "" ? "" : ", ") + token;
209  }
210  }
211  finalizedBudget.SetProposalsStr(strProposals);
212 }
213 
214 std::string CBudgetManager::GetFinalizedBudgetStatus(const uint256& nHash) const
215 {
216  CFinalizedBudget fb;
217  if (!GetFinalizedBudget(nHash, fb))
218  return strprintf("ERROR: cannot find finalized budget %s\n", nHash.ToString());
219 
220  std::string retBadHashes = "";
221  std::string retBadPayeeOrAmount = "";
222  int nBlockStart = fb.GetBlockStart();
223  int nBlockEnd = fb.GetBlockEnd();
224 
225  for (int nBlockHeight = nBlockStart; nBlockHeight <= nBlockEnd; nBlockHeight++) {
226  CTxBudgetPayment budgetPayment;
227  if (!fb.GetBudgetPaymentByBlock(nBlockHeight, budgetPayment)) {
228  LogPrint(BCLog::MNBUDGET,"%s: Couldn't find budget payment for block %lld\n", __func__, nBlockHeight);
229  continue;
230  }
231 
232  CBudgetProposal bp;
233  if (!GetProposal(budgetPayment.nProposalHash, bp)) {
234  retBadHashes += (retBadHashes == "" ? "" : ", ") + budgetPayment.nProposalHash.ToString();
235  continue;
236  }
237 
238  if (bp.GetPayee() != budgetPayment.payee || bp.GetAmount() != budgetPayment.nAmount) {
239  retBadPayeeOrAmount += (retBadPayeeOrAmount == "" ? "" : ", ") + budgetPayment.nProposalHash.ToString();
240  }
241  }
242 
243  if (retBadHashes == "" && retBadPayeeOrAmount == "") return "OK";
244 
245  if (retBadHashes != "") retBadHashes = "Unknown proposal(s) hash! Check this proposal(s) before voting: " + retBadHashes;
246  if (retBadPayeeOrAmount != "") retBadPayeeOrAmount = "Budget payee/nAmount doesn't match our proposal(s)! "+ retBadPayeeOrAmount;
247 
248  return retBadHashes + " -- " + retBadPayeeOrAmount;
249 }
250 
252 {
253  AssertLockNotHeld(cs_budgets); // need to lock cs_main here (CheckCollateral)
254  const uint256& nHash = finalizedBudget.GetHash();
255 
256  if (WITH_LOCK(cs_budgets, return mapFinalizedBudgets.count(nHash))) {
257  LogPrint(BCLog::MNBUDGET,"%s: finalized budget %s already added\n", __func__, nHash.ToString());
258  return false;
259  }
260 
261  if (!finalizedBudget.IsWellFormed(GetTotalBudget(finalizedBudget.GetBlockStart()))) {
262  LogPrint(BCLog::MNBUDGET,"%s: invalid finalized budget: %s %s\n", __func__, nHash.ToString(), finalizedBudget.IsInvalidLogStr());
263  return false;
264  }
265 
266  std::string strError;
267  int nCurrentHeight = GetBestHeight();
268  const uint256& feeTxId = finalizedBudget.GetFeeTXHash();
269  if (!CheckCollateral(feeTxId, nHash, strError, finalizedBudget.nTime, nCurrentHeight, true)) {
270  LogPrint(BCLog::MNBUDGET,"%s: invalid finalized budget (%s) collateral id=%s - %s\n",
271  __func__, nHash.ToString(), feeTxId.ToString(), strError);
272  finalizedBudget.SetStrInvalid(strError);
273  return false;
274  }
275 
276  // update expiration
277  if (!finalizedBudget.UpdateValid(nCurrentHeight)) {
278  LogPrint(BCLog::MNBUDGET,"%s: invalid finalized budget: %s %s\n", __func__, nHash.ToString(), finalizedBudget.IsInvalidLogStr());
279  return false;
280  }
281 
282  // Compare budget payments with existent proposals, don't care on the order, just verify proposals existence.
283  std::vector<CBudgetProposal> vBudget = GetBudget();
284  std::map<uint256, CBudgetProposal> mapWinningProposals;
285  for (const CBudgetProposal& p: vBudget) { mapWinningProposals.emplace(p.GetHash(), p); }
286  if (!finalizedBudget.CheckProposals(mapWinningProposals)) {
287  finalizedBudget.SetStrInvalid("Invalid proposals");
288  LogPrint(BCLog::MNBUDGET,"%s: Budget finalization does not match with winning proposals\n", __func__);
289  // just for now (until v6), request proposals and budget sync in case we are missing them
290  if (pfrom) {
291  CNetMsgMaker maker(pfrom->GetSendVersion());
292  // First, request single proposals that we don't have.
293  for (const auto& propId : finalizedBudget.GetProposalsHashes()) {
294  if (!g_budgetman.HaveProposal(propId)) {
295  g_connman->PushMessage(pfrom, maker.Make(NetMsgType::BUDGETVOTESYNC, propId));
296  }
297  }
298 
299  // Second a full budget sync for missing votes and the budget finalization that we are rejecting here.
300  // Note: this will not make any effect on peers with version <= 70923 as they, invalidly, are blocking
301  // follow-up budget sync request for the entire node life cycle.
302  uint256 n;
303  g_connman->PushMessage(pfrom, maker.Make(NetMsgType::BUDGETVOTESYNC, n));
304  }
305  return false;
306  }
307 
308  // Add budget finalization.
309  SetBudgetProposalsStr(finalizedBudget);
310  ForceAddFinalizedBudget(nHash, feeTxId, finalizedBudget);
311 
312  LogPrint(BCLog::MNBUDGET,"%s: finalized budget %s [%s (%s)] added\n",
313  __func__, nHash.ToString(), finalizedBudget.GetName(), finalizedBudget.GetProposalsStr());
314  return true;
315 }
316 
317 void CBudgetManager::ForceAddFinalizedBudget(const uint256& nHash, const uint256& feeTxId, const CFinalizedBudget& finalizedBudget)
318 {
319  LOCK(cs_budgets);
320  mapFinalizedBudgets.emplace(nHash, finalizedBudget);
321  // Add to feeTx index
322  mapFeeTxToBudget.emplace(feeTxId, nHash);
323  // Remove the budget from the unconfirmed map, if it was there
324  if (mapUnconfirmedFeeTx.count(nHash))
325  mapUnconfirmedFeeTx.erase(nHash);
326 }
327 
329 {
330  AssertLockNotHeld(cs_proposals); // need to lock cs_main here (CheckCollateral)
331  const uint256& nHash = budgetProposal.GetHash();
332 
333  if (WITH_LOCK(cs_proposals, return mapProposals.count(nHash))) {
334  LogPrint(BCLog::MNBUDGET,"%s: proposal %s already added\n", __func__, nHash.ToString());
335  return false;
336  }
337 
338  if (!budgetProposal.IsWellFormed(GetTotalBudget(budgetProposal.GetBlockStart()))) {
339  LogPrint(BCLog::MNBUDGET,"%s: Invalid budget proposal %s %s\n", __func__, nHash.ToString(), budgetProposal.IsInvalidLogStr());
340  return false;
341  }
342 
343  std::string strError;
344  int nCurrentHeight = GetBestHeight();
345  const uint256& feeTxId = budgetProposal.GetFeeTXHash();
346  if (!CheckCollateral(feeTxId, nHash, strError, budgetProposal.nTime, nCurrentHeight, false)) {
347  LogPrint(BCLog::MNBUDGET,"%s: invalid budget proposal (%s) collateral id=%s - %s\n",
348  __func__, nHash.ToString(), feeTxId.ToString(), strError);
349  budgetProposal.SetStrInvalid(strError);
350  return false;
351  }
352 
353  // update expiration / heavily-downvoted
354  int mnCount = mnodeman.CountEnabled();
355  if (!budgetProposal.UpdateValid(nCurrentHeight, mnCount)) {
356  LogPrint(BCLog::MNBUDGET,"%s: Invalid budget proposal %s %s\n", __func__, nHash.ToString(), budgetProposal.IsInvalidLogStr());
357  return false;
358  }
359 
360  {
362  mapProposals.emplace(nHash, budgetProposal);
363  // Add to feeTx index
364  mapFeeTxToProposal.emplace(feeTxId, nHash);
365  }
366  LogPrint(BCLog::MNBUDGET,"%s: budget proposal %s [%s] added\n", __func__, nHash.ToString(), budgetProposal.GetName());
367 
368  return true;
369 }
370 
372 {
373  int nCurrentHeight = GetBestHeight();
374  std::map<uint256, CFinalizedBudget> tmpMapFinalizedBudgets;
375  std::map<uint256, CBudgetProposal> tmpMapProposals;
376 
377  // Get MN count, used for the heavily down-voted check
378  int mnCount = mnodeman.CountEnabled();
379 
380  // Check Proposals first
381  {
383  LogPrint(BCLog::MNBUDGET, "%s: mapProposals cleanup - size before: %d\n", __func__, mapProposals.size());
384  for (auto& it: mapProposals) {
385  CBudgetProposal* pbudgetProposal = &(it.second);
386  if (!pbudgetProposal->UpdateValid(nCurrentHeight, mnCount)) {
387  LogPrint(BCLog::MNBUDGET,"%s: Invalid budget proposal %s %s\n", __func__, (it.first).ToString(), pbudgetProposal->IsInvalidLogStr());
388  mapFeeTxToProposal.erase(pbudgetProposal->GetFeeTXHash());
389  } else {
390  LogPrint(BCLog::MNBUDGET,"%s: Found valid budget proposal: %s %s\n", __func__,
391  pbudgetProposal->GetName(), pbudgetProposal->GetFeeTXHash().ToString());
392  tmpMapProposals.emplace(pbudgetProposal->GetHash(), *pbudgetProposal);
393  }
394  }
395  // Remove invalid entries by overwriting complete map
396  mapProposals.swap(tmpMapProposals);
397  LogPrint(BCLog::MNBUDGET, "%s: mapProposals cleanup - size after: %d\n", __func__, mapProposals.size());
398  }
399 
400  // Then check finalized budgets
401  {
402  LOCK(cs_budgets);
403  LogPrint(BCLog::MNBUDGET, "%s: mapFinalizedBudgets cleanup - size before: %d\n", __func__, mapFinalizedBudgets.size());
404  for (auto& it: mapFinalizedBudgets) {
405  CFinalizedBudget* pfinalizedBudget = &(it.second);
406  if (!pfinalizedBudget->UpdateValid(nCurrentHeight)) {
407  LogPrint(BCLog::MNBUDGET,"%s: Invalid finalized budget %s %s\n", __func__, (it.first).ToString(), pfinalizedBudget->IsInvalidLogStr());
408  mapFeeTxToBudget.erase(pfinalizedBudget->GetFeeTXHash());
409  } else {
410  LogPrint(BCLog::MNBUDGET,"%s: Found valid finalized budget: %s %s\n", __func__,
411  pfinalizedBudget->GetName(), pfinalizedBudget->GetFeeTXHash().ToString());
412  tmpMapFinalizedBudgets.emplace(pfinalizedBudget->GetHash(), *pfinalizedBudget);
413  }
414  }
415  // Remove invalid entries by overwriting complete map
416  mapFinalizedBudgets = tmpMapFinalizedBudgets;
417  LogPrint(BCLog::MNBUDGET, "%s: mapFinalizedBudgets cleanup - size after: %d\n", __func__, mapFinalizedBudgets.size());
418  }
419  // Masternodes vote on valid ones
421 }
422 
424 {
425  {
427  // Is this collateral related to a proposal?
428  const auto& it = mapFeeTxToProposal.find(feeTxId);
429  if (it != mapFeeTxToProposal.end()) {
430  // Remove proposal
431  CBudgetProposal* p = FindProposal(it->second);
432  if (p) {
433  LogPrintf("%s: Removing proposal %s (collateral disconnected, id=%s)\n", __func__, p->GetName(), feeTxId.ToString());
434  {
435  // Erase seen/orphan votes
436  LOCK(cs_votes);
437  for (const auto& vote: p->GetVotes()) {
438  const uint256& hash{vote.second.GetHash()};
439  mapSeenProposalVotes.erase(hash);
440  mapOrphanProposalVotes.erase(hash);
441  }
442  }
443  // Erase proposal object
444  mapProposals.erase(it->second);
445  }
446  // Remove from collateral index
447  mapFeeTxToProposal.erase(it);
448  return;
449  }
450  }
451  {
452  LOCK(cs_budgets);
453  // Is this collateral related to a finalized budget?
454  const auto& it = mapFeeTxToBudget.find(feeTxId);
455  if (it != mapFeeTxToBudget.end()) {
456  // Remove finalized budget
457  CFinalizedBudget* b = FindFinalizedBudget(it->second);
458  if (b) {
459  LogPrintf("%s: Removing finalized budget %s (collateral disconnected, id=%s)\n", __func__, b->GetName(), feeTxId.ToString());
460  {
461  // Erase seen/orphan votes
463  for (const uint256& hash: b->GetVotesHashes()) {
464  mapSeenFinalizedBudgetVotes.erase(hash);
465  mapOrphanFinalizedBudgetVotes.erase(hash);
466  }
467  }
468  // Erase finalized budget object
469  mapFinalizedBudgets.erase(it->second);
470  }
471  // Remove from collateral index
472  mapFeeTxToBudget.erase(it);
473  }
474  }
475 }
476 
478 {
479  LOCK(cs_budgets);
480  int highestVoteCount = 0;
481  const CFinalizedBudget* pHighestBudget = nullptr;
482  for (const auto& it: mapFinalizedBudgets) {
483  const CFinalizedBudget* pfinalizedBudget = &(it.second);
484  int voteCount = pfinalizedBudget->GetVoteCount();
485  if (voteCount > highestVoteCount &&
486  chainHeight >= pfinalizedBudget->GetBlockStart() &&
487  chainHeight <= pfinalizedBudget->GetBlockEnd()) {
488  pHighestBudget = pfinalizedBudget;
489  highestVoteCount = voteCount;
490  }
491  }
492  return {pHighestBudget, highestVoteCount};
493 }
494 
495 int CBudgetManager::GetHighestVoteCount(int chainHeight) const
496 {
497  const auto& highestBudFin = GetBudgetWithHighestVoteCount(chainHeight);
498  return (highestBudFin.m_budget_fin ? highestBudFin.m_vote_count : -1);
499 }
500 
501 bool CBudgetManager::GetPayeeAndAmount(int chainHeight, CScript& payeeRet, CAmount& nAmountRet) const
502 {
503  int nCountThreshold;
504  if (!IsBudgetPaymentBlock(chainHeight, nCountThreshold))
505  return false;
506 
507  const auto& highestBudFin = GetBudgetWithHighestVoteCount(chainHeight);
508  const CFinalizedBudget* pfb = highestBudFin.m_budget_fin;
509  return pfb && pfb->GetPayeeAndAmount(chainHeight, payeeRet, nAmountRet) && highestBudFin.m_vote_count > nCountThreshold;
510 }
511 
512 bool CBudgetManager::GetExpectedPayeeAmount(int chainHeight, CAmount& nAmountRet) const
513 {
514  CScript payeeRet;
515  return GetPayeeAndAmount(chainHeight, payeeRet, nAmountRet);
516 }
517 
518 bool CBudgetManager::FillBlockPayee(CMutableTransaction& txCoinbase, CMutableTransaction& txCoinstake, const int nHeight, bool fProofOfStake) const
519 {
520  if (nHeight <= 0) return false;
521 
522  CScript payee;
523  CAmount nAmount = 0;
524 
525  if (!GetPayeeAndAmount(nHeight, payee, nAmount))
526  return false;
527 
528  CAmount blockValue = GetBlockValue(nHeight);
529 
530  // Starting from PIVX v6.0 masternode and budgets are paid in the coinbase tx of PoS blocks
531  const bool fPayCoinstake = fProofOfStake &&
533 
534  if (fProofOfStake) {
535  if (fPayCoinstake) {
536  unsigned int i = txCoinstake.vout.size();
537  txCoinstake.vout.resize(i + 1);
538  txCoinstake.vout[i].scriptPubKey = payee;
539  txCoinstake.vout[i].nValue = nAmount;
540  } else {
541  txCoinbase.vout.resize(1);
542  txCoinbase.vout[0].scriptPubKey = payee;
543  txCoinbase.vout[0].nValue = nAmount;
544  }
545  } else {
546  //miners get the full amount on these blocks
547  txCoinbase.vout[0].nValue = blockValue;
548  txCoinbase.vout.resize(2);
549 
550  //these are super blocks, so their value can be much larger than normal
551  txCoinbase.vout[1].scriptPubKey = payee;
552  txCoinbase.vout[1].nValue = nAmount;
553  }
554 
555  CTxDestination address;
556  ExtractDestination(payee, address);
557  LogPrint(BCLog::MNBUDGET,"%s: Budget payment to %s for %lld\n", __func__, EncodeDestination(address), nAmount);
558  return true;
559 }
560 
562 {
563  // function called only from initialized masternodes
564  if (!fMasterNode) {
565  LogPrint(BCLog::MNBUDGET,"%s: Not a masternode\n", __func__);
566  return;
567  }
568 
569  // Do this 1 in 4 blocks -- spread out the voting activity
570  // -- this function is only called every fourteenth block, so this is really 1 in 56 blocks
571  if (GetRandInt(4) != 0) {
572  LogPrint(BCLog::MNBUDGET,"%s: waiting\n", __func__);
573  return;
574  }
575 
576  // Get the active masternode (operator) key
577  CTxIn mnVin;
578  Optional<CKey> mnKey{nullopt};
579  CBLSSecretKey blsKey;
580  if (!GetActiveMasternodeKeys(mnVin, mnKey, blsKey)) {
581  return;
582  }
583 
584  std::vector<CBudgetProposal> vBudget = GetBudget();
585  if (vBudget.empty()) {
586  LogPrint(BCLog::MNBUDGET,"%s: No proposal can be finalized\n", __func__);
587  return;
588  }
589 
590  std::map<uint256, CBudgetProposal> mapWinningProposals;
591  for (const CBudgetProposal& p: vBudget) {
592  mapWinningProposals.emplace(p.GetHash(), p);
593  }
594  // Vector containing the hash of finalized budgets to sign
595  std::vector<uint256> vBudgetHashes;
596  {
597  LOCK(cs_budgets);
598  for (auto& it: mapFinalizedBudgets) {
599  CFinalizedBudget* pfb = &(it.second);
600  // we only need to check this once
601  if (pfb->IsAutoChecked()) continue;
602  pfb->SetAutoChecked(true);
603  //only vote for exact matches
604  if (strBudgetMode == "auto") {
605  // compare budget payments with winning proposals
606  if (!pfb->CheckProposals(mapWinningProposals)) {
607  continue;
608  }
609  }
610  // exact match found. add budget hash to sign it later.
611  vBudgetHashes.emplace_back(pfb->GetHash());
612  }
613  }
614 
615  // Sign finalized budgets
616  for (const uint256& budgetHash: vBudgetHashes) {
617  CFinalizedBudgetVote vote(mnVin, budgetHash);
618  if (mnKey != nullopt) {
619  // Legacy MN
620  if (!vote.Sign(*mnKey, mnKey->GetPubKey().GetID())) {
621  LogPrintf("%s: Failure to sign budget %s\n", __func__, budgetHash.ToString());
622  continue;
623  }
624  } else {
625  // DMN
626  if (!vote.Sign(blsKey)) {
627  LogPrintf("%s: Failure to sign budget %s with DMN\n", __func__, budgetHash.ToString());
628  continue;
629  }
630  }
631  std::string strError = "";
632  if (!UpdateFinalizedBudget(vote, nullptr, strError)) {
633  LogPrintf("%s: Error submitting vote - %s\n", __func__, strError);
634  continue;
635  }
636  LogPrint(BCLog::MNBUDGET, "%s: new finalized budget vote signed: %s\n", __func__, vote.GetHash().ToString());
638  vote.Relay();
639  }
640 }
641 
643 {
645  auto it = mapFinalizedBudgets.find(nHash);
646  return it != mapFinalizedBudgets.end() ? &(it->second) : nullptr;
647 }
648 
649 const CBudgetProposal* CBudgetManager::FindProposalByName(const std::string& strProposalName) const
650 {
652 
653  int64_t nYesCountMax = std::numeric_limits<int64_t>::min();
654  const CBudgetProposal* pbudgetProposal = nullptr;
655 
656  for (const auto& it: mapProposals) {
657  const CBudgetProposal& proposal = it.second;
658  int64_t nYesCount = proposal.GetYeas() - proposal.GetNays();
659  if (proposal.GetName() == strProposalName && nYesCount > nYesCountMax) {
660  pbudgetProposal = &proposal;
661  nYesCountMax = nYesCount;
662  }
663  }
664 
665  return pbudgetProposal;
666 }
667 
669 {
671  auto it = mapProposals.find(nHash);
672  return it != mapProposals.end() ? &(it->second) : nullptr;
673 }
674 
676 {
678  auto it = mapProposals.find(nHash);
679  if (it == mapProposals.end()) return false;
680  bp = it->second;
681  return true;
682 }
683 
685 {
686  LOCK(cs_budgets);
687  auto it = mapFinalizedBudgets.find(nHash);
688  if (it == mapFinalizedBudgets.end()) return false;
689  fb = it->second;
690  return true;
691 }
692 
693 bool CBudgetManager::IsBudgetPaymentBlock(int nBlockHeight, int& nCountThreshold) const
694 {
695  int nHighestCount = GetHighestVoteCount(nBlockHeight);
696  int nCountEnabled = mnodeman.CountEnabled();
697  int nFivePercent = nCountEnabled / 20;
698  // threshold for highest finalized budgets (highest vote count - 10% of active masternodes)
699  nCountThreshold = nHighestCount - (nCountEnabled / 10);
700  // reduce the threshold if there are less than 10 enabled masternodes
701  if (nCountThreshold == nHighestCount) nCountThreshold--;
702 
703  LogPrint(BCLog::MNBUDGET,"%s: nHighestCount: %lli, 5%% of Masternodes: %lli.\n",
704  __func__, nHighestCount, nFivePercent);
705 
706  // If budget doesn't have 5% of the network votes, then we should pay a masternode instead
707  return (nHighestCount > nFivePercent);
708 }
709 
710 bool CBudgetManager::IsBudgetPaymentBlock(int nBlockHeight) const
711 {
712  int nCountThreshold;
713  return IsBudgetPaymentBlock(nBlockHeight, nCountThreshold);
714 }
715 
716 TrxValidationStatus CBudgetManager::IsTransactionValid(const CTransaction& txNew, const uint256& nBlockHash, int nBlockHeight) const
717 {
718  int nCountThreshold = 0;
719  if (!IsBudgetPaymentBlock(nBlockHeight, nCountThreshold)) {
720  // If budget doesn't have 5% of the network votes, then we should pay a masternode instead
722  }
723 
724  // check the highest finalized budgets (- 10% to assist in consensus)
725  bool fThreshold = false;
726  {
727  LOCK(cs_budgets);
728  // Get the finalized budget with the highest amount of votes..
729  const auto& highestBudFin = GetBudgetWithHighestVoteCount(nBlockHeight);
730  const CFinalizedBudget* highestVotesBudget = highestBudFin.m_budget_fin;
731  if (highestVotesBudget) {
732  // Need to surpass the threshold
733  if (highestBudFin.m_vote_count > nCountThreshold) {
734  fThreshold = true;
735  if (highestVotesBudget->IsTransactionValid(txNew, nBlockHash, nBlockHeight) ==
738  }
739  }
740  // tx not valid
741  LogPrint(BCLog::MNBUDGET, "%s: ignoring budget. Out of range or tx not valid.\n", __func__);
742  }
743  }
744 
745  // If not enough masternodes autovoted for any of the finalized budgets or if none of the txs
746  // are valid, we should pay a masternode instead
748 }
749 
750 std::vector<CBudgetProposal*> CBudgetManager::GetAllProposalsOrdered()
751 {
753  std::vector<CBudgetProposal*> vBudgetProposalRet;
754  for (auto& it: mapProposals) {
755  CBudgetProposal* pbudgetProposal = &(it.second);
756  RemoveStaleVotesOnProposal(pbudgetProposal);
757  vBudgetProposalRet.push_back(pbudgetProposal);
758  }
759  std::sort(vBudgetProposalRet.begin(), vBudgetProposalRet.end(), CBudgetProposal::PtrHigherYes);
760  return vBudgetProposalRet;
761 }
762 
763 std::vector<CBudgetProposal> CBudgetManager::GetBudget()
764 {
766 
767  int nHeight = GetBestHeight();
768  if (nHeight <= 0)
769  return {};
770 
771  // ------- Get proposals ordered by votes (highest to lowest)
772  std::vector<CBudgetProposal*> vProposalsOrdered = GetAllProposalsOrdered();
773 
774  // ------- Grab The Budgets In Order
775  std::vector<CBudgetProposal> vBudgetProposalsRet;
776  CAmount nBudgetAllocated = 0;
777 
778  const int nBlocksPerCycle = Params().GetConsensus().nBudgetCycleBlocks;
779  int nBlockStart = nHeight - nHeight % nBlocksPerCycle + nBlocksPerCycle;
780  int nBlockEnd = nBlockStart + nBlocksPerCycle - 1;
781  int mnCount = mnodeman.CountEnabled();
782  CAmount nTotalBudget = GetTotalBudget(nBlockStart);
783 
784  for (CBudgetProposal* pbudgetProposal: vProposalsOrdered) {
785  LogPrint(BCLog::MNBUDGET,"%s: Processing Budget %s\n", __func__, pbudgetProposal->GetName());
786  //prop start/end should be inside this period
787  if (pbudgetProposal->IsPassing(nBlockStart, nBlockEnd, mnCount)) {
788  LogPrint(BCLog::MNBUDGET,"%s: - Check 1 passed: valid=%d | %ld <= %ld | %ld >= %ld | Yeas=%d Nays=%d Count=%d | established=%d\n",
789  __func__, pbudgetProposal->IsValid(), pbudgetProposal->GetBlockStart(), nBlockStart, pbudgetProposal->GetBlockEnd(),
790  nBlockEnd, pbudgetProposal->GetYeas(), pbudgetProposal->GetNays(), mnCount / 10, pbudgetProposal->IsEstablished());
791 
792  if (pbudgetProposal->GetAmount() + nBudgetAllocated <= nTotalBudget) {
793  pbudgetProposal->SetAllotted(pbudgetProposal->GetAmount());
794  nBudgetAllocated += pbudgetProposal->GetAmount();
795  vBudgetProposalsRet.emplace_back(*pbudgetProposal);
796  LogPrint(BCLog::MNBUDGET,"%s: - Check 2 passed: Budget added\n", __func__);
797  } else {
798  pbudgetProposal->SetAllotted(0);
799  LogPrint(BCLog::MNBUDGET,"%s: - Check 2 failed: no amount allotted\n", __func__);
800  }
801 
802  } else {
803  LogPrint(BCLog::MNBUDGET,"%s: - Check 1 failed: valid=%d | %ld <= %ld | %ld >= %ld | Yeas=%d Nays=%d Count=%d | established=%d\n",
804  __func__, pbudgetProposal->IsValid(), pbudgetProposal->GetBlockStart(), nBlockStart, pbudgetProposal->GetBlockEnd(),
805  nBlockEnd, pbudgetProposal->GetYeas(), pbudgetProposal->GetNays(), mnodeman.CountEnabled() / 10,
806  pbudgetProposal->IsEstablished());
807  }
808 
809  }
810 
811  return vBudgetProposalsRet;
812 }
813 
814 std::vector<CFinalizedBudget*> CBudgetManager::GetFinalizedBudgets()
815 {
816  LOCK(cs_budgets);
817 
818  std::vector<CFinalizedBudget*> vFinalizedBudgetsRet;
819 
820  // ------- Grab The Budgets In Order
821  for (auto& it: mapFinalizedBudgets) {
822  vFinalizedBudgetsRet.push_back(&(it.second));
823  }
824  std::sort(vFinalizedBudgetsRet.begin(), vFinalizedBudgetsRet.end(), CFinalizedBudget::PtrGreater);
825 
826  return vFinalizedBudgetsRet;
827 }
828 
829 std::string CBudgetManager::GetRequiredPaymentsString(int nBlockHeight)
830 {
831  LOCK(cs_budgets);
832 
833  std::string ret = "unknown-budget";
834 
835  std::map<uint256, CFinalizedBudget>::iterator it = mapFinalizedBudgets.begin();
836  while (it != mapFinalizedBudgets.end()) {
837  CFinalizedBudget* pfinalizedBudget = &((*it).second);
838  if (nBlockHeight >= pfinalizedBudget->GetBlockStart() && nBlockHeight <= pfinalizedBudget->GetBlockEnd()) {
839  CTxBudgetPayment payment;
840  if (pfinalizedBudget->GetBudgetPaymentByBlock(nBlockHeight, payment)) {
841  if (ret == "unknown-budget") {
842  ret = payment.nProposalHash.ToString();
843  } else {
844  ret += ",";
845  ret += payment.nProposalHash.ToString();
846  }
847  } else {
848  LogPrint(BCLog::MNBUDGET,"%s: Couldn't find budget payment for block %d\n", __func__, nBlockHeight);
849  }
850  }
851 
852  ++it;
853  }
854 
855  return ret;
856 }
857 
859 {
860  // 100% of block reward after V5.5 upgrade
861  CAmount nSubsidy = GetBlockValue(nHeight);
862 
863  // 20% of block reward prior to V5.5 upgrade
864  if (nHeight <= Params().GetConsensus().vUpgrades[Consensus::UPGRADE_V5_5].nActivationHeight) {
865  nSubsidy /= 5;
866  }
867 
868  // multiplied by the number of blocks in a cycle (144 on testnet, 30*1440 on mainnet)
869  return nSubsidy * Params().GetConsensus().nBudgetCycleBlocks;
870 }
871 
873 {
874  LOCK(cs_votes);
875  mapSeenProposalVotes.emplace(vote.GetHash(), vote);
876 }
877 
879 {
881  mapSeenFinalizedBudgetVotes.emplace(vote.GetHash(), vote);
882 }
883 
885 {
887  LogPrint(BCLog::MNBUDGET, "Cleaning proposal votes for %s. Before: YES=%d, NO=%d\n",
888  prop->GetName(), prop->GetYeas(), prop->GetNays());
889 
890  auto it = prop->mapVotes.begin();
891  while (it != prop->mapVotes.end()) {
892  auto mnList = deterministicMNManager->GetListAtChainTip();
893  auto dmn = mnList.GetMNByCollateral(it->first);
894  if (dmn) {
895  (*it).second.SetValid(!dmn->IsPoSeBanned());
896  } else {
897  // -- Legacy System (!TODO: remove after enforcement) --
898  CMasternode* pmn = mnodeman.Find(it->first);
899  (*it).second.SetValid(pmn && pmn->IsEnabled());
900  }
901  ++it;
902  }
903 
904  LogPrint(BCLog::MNBUDGET, "Cleaned proposal votes for %s. After: YES=%d, NO=%d\n",
905  prop->GetName(), prop->GetYeas(), prop->GetNays());
906 }
907 
909 {
911  LogPrint(BCLog::MNBUDGET, "Cleaning finalized budget votes for [%s (%s)]. Before: %d\n",
912  fbud->GetName(), fbud->GetProposalsStr(), fbud->GetVoteCount());
913 
914  auto it = fbud->mapVotes.begin();
915  while (it != fbud->mapVotes.end()) {
916  auto mnList = deterministicMNManager->GetListAtChainTip();
917  auto dmn = mnList.GetMNByCollateral(it->first);
918  if (dmn) {
919  (*it).second.SetValid(!dmn->IsPoSeBanned());
920  } else {
921  // -- Legacy System (!TODO: remove after enforcement) --
922  CMasternode* pmn = mnodeman.Find(it->first);
923  (*it).second.SetValid(pmn && pmn->IsEnabled());
924  }
925  ++it;
926  }
927  LogPrint(BCLog::MNBUDGET, "Cleaned finalized budget votes for [%s (%s)]. After: %d\n",
928  fbud->GetName(), fbud->GetProposalsStr(), fbud->GetVoteCount());
929 }
930 
932 {
933  LOCK(cs_votes);
934  CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
935  ss.reserve(1000);
936  ss << mapSeenProposalVotes.at(voteHash);
937  return ss;
938 }
939 
941 {
943  return mapProposals.at(propHash).GetBroadcast();
944 }
945 
947 {
949  CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
950  ss.reserve(1000);
951  ss << mapSeenFinalizedBudgetVotes.at(voteHash);
952  return ss;
953 }
954 
956 {
957  LOCK(cs_budgets);
958  return mapFinalizedBudgets.at(budgetHash).GetBroadcast();
959 }
960 
961 bool CBudgetManager::AddAndRelayProposalVote(const CBudgetVote& vote, std::string& strError)
962 {
963  if (UpdateProposal(vote, nullptr, strError)) {
964  AddSeenProposalVote(vote);
965  vote.Relay();
966  return true;
967  }
968  return false;
969 }
970 
971 void CBudgetManager::UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload)
972 {
974 
975  if (strBudgetMode == "suggest") { //suggest the budget we see
977  }
978 
979  int nCurrentHeight = GetBestHeight();
980  //this function should be called 1/14 blocks, allowing up to 100 votes per day on all proposals
981  if (nCurrentHeight % 14 != 0) return;
982 
983  // incremental sync with our peers
985  LogPrint(BCLog::MNBUDGET,"%s: incremental sync started\n", __func__);
986  // Once every 7 days, try to relay the complete budget data
987  if (GetRandInt(Params().IsRegTestNet() ? 2 : 720) == 0) {
988  ResetSync();
989  }
990 
991  CBudgetManager* manager = this;
992  g_connman->ForEachNode([manager](CNode* pnode){
993  if (pnode->nVersion >= ActiveProtocol())
994  manager->Sync(pnode, true);
995  });
996  MarkSynced();
997  }
998 
999  // remove expired/heavily downvoted budgets
1000  CheckAndRemove();
1001 
1002  {
1003  LOCK(cs_proposals);
1004  LogPrint(BCLog::MNBUDGET,"%s: mapProposals cleanup - size: %d\n", __func__, mapProposals.size());
1005  for (auto& it: mapProposals) {
1006  RemoveStaleVotesOnProposal(&it.second);
1007  }
1008  }
1009  {
1010  LOCK(cs_budgets);
1011  LogPrint(BCLog::MNBUDGET,"%s: mapFinalizedBudgets cleanup - size: %d\n", __func__, mapFinalizedBudgets.size());
1012  for (auto& it: mapFinalizedBudgets) {
1013  RemoveStaleVotesOnFinalBudget(&it.second);
1014  }
1015  }
1016 
1017  int64_t now = GetTime();
1018  const auto cleanOrphans = [now](auto& mutex, auto& mapOrphans, auto& mapSeen) {
1019  LOCK(mutex);
1020  for (auto it = mapOrphans.begin() ; it != mapOrphans.end();) {
1021  int64_t lastReceivedVoteTime = it->second.second;
1022  if (lastReceivedVoteTime + BUDGET_ORPHAN_VOTES_CLEANUP_SECONDS < now) {
1023  // Clean seen votes
1024  for (const auto& voteIt : it->second.first) {
1025  mapSeen.erase(voteIt.GetHash());
1026  }
1027  // Remove proposal orphan votes
1028  it = mapOrphans.erase(it);
1029  } else {
1030  it++;
1031  }
1032  }
1033  };
1034 
1035  // Clean orphan proposal votes if no parent arrived after an hour.
1037  // Clean orphan budget votes if no parent arrived after an hour.
1039 
1040  // Once every 2 weeks (1/14 * 1/1440), clean the seen maps
1041  if (g_tiertwo_sync_state.IsSynced() && GetRandInt(1440) == 0) {
1042  ReloadMapSeen();
1043  }
1044 
1045  LogPrint(BCLog::MNBUDGET,"%s: PASSED\n", __func__);
1046 }
1047 
1049 {
1050  if (nProp.IsNull()) {
1052  if (!(pfrom->addr.IsRFC1918() || pfrom->addr.IsLocal())) {
1053  if (g_netfulfilledman.HasFulfilledRequest(pfrom->addr, BUDGET_SYNC_REQUEST_RECV)) {
1054  LogPrint(BCLog::MASTERNODE, "budgetsync - peer %i already asked for budget sync\n", pfrom->GetId());
1055  // let's not be so hard with the node for now.
1056  return 10;
1057  }
1058  }
1059  }
1060 
1061  if (nProp.IsNull()) Sync(pfrom, false /* fPartial */);
1062  else SyncSingleItem(pfrom, nProp);
1063  LogPrint(BCLog::MNBUDGET, "mnvs - Sent Masternode votes to peer %i\n", pfrom->GetId());
1064  return 0;
1065 }
1066 
1068 {
1069  const uint256& nHash = proposal.GetHash();
1070  if (HaveProposal(nHash)) {
1072  return 0;
1073  }
1074  if (!AddProposal(proposal)) {
1075  return 0;
1076  }
1077 
1078  // Relay only if we are synchronized
1079  // Makes no sense to relay proposals to the peers from where we are syncing them.
1080  if (g_tiertwo_sync_state.IsSynced()) proposal.Relay();
1082 
1083  LogPrint(BCLog::MNBUDGET, "mprop (new) %s\n", nHash.ToString());
1084  //We might have active votes for this proposal that are valid now
1085  CheckOrphanVotes();
1086  return 0;
1087 }
1088 
1090 {
1091  const uint256& voteID = vote.GetHash();
1092 
1093  if (HaveSeenProposalVote(voteID)) {
1095  return false;
1096  }
1097 
1098  std::string err;
1099  if (vote.GetTime() > GetTime() + (60 * 60)) {
1100  err = strprintf("new vote is too far ahead of current time - %s - nTime %lli - Max Time %lli\n",
1101  vote.GetHash().ToString(), vote.GetTime(), GetTime() + (60 * 60));
1102  return state.Invalid(false, REJECT_INVALID, "bad-mvote", err);
1103  }
1104 
1105  const CTxIn& voteVin = vote.GetVin();
1106 
1107  // See if this vote was signed with a deterministic masternode
1108  auto mnList = deterministicMNManager->GetListAtChainTip();
1109  auto dmn = mnList.GetMNByCollateral(voteVin.prevout);
1110  if (dmn) {
1111  const std::string& mn_protx_id = dmn->proTxHash.ToString();
1112 
1113  if (dmn->IsPoSeBanned()) {
1114  err = strprintf("masternode (%s) not valid or PoSe banned", mn_protx_id);
1115  return state.DoS(0, false, REJECT_INVALID, "bad-mvote", false, err);
1116  }
1117 
1118  AddSeenProposalVote(vote);
1119 
1120  if (!vote.CheckSignature(dmn->pdmnState->keyIDVoting)) {
1121  err = strprintf("invalid mvote sig from dmn: %s", mn_protx_id);
1122  return state.DoS(100, false, REJECT_INVALID, "bad-mvote-sig", false, err);
1123  }
1124 
1125  if (!UpdateProposal(vote, pfrom, err)) {
1126  return state.DoS(0, false, REJECT_INVALID, "bad-mvote", false, strprintf("%s (%s)", err, mn_protx_id));
1127  }
1128 
1129  // Relay only if we are synchronized
1130  // Makes no sense to relay votes to the peers from where we are syncing them.
1131  if (g_tiertwo_sync_state.IsSynced()) vote.Relay();
1133  LogPrint(BCLog::MNBUDGET, "mvote - new vote (%s) for proposal %s from dmn %s\n",
1134  voteID.ToString(), vote.GetProposalHash().ToString(), mn_protx_id);
1135  return true;
1136  }
1137 
1138  // -- Legacy System (!TODO: remove after enforcement) --
1139 
1140  CMasternode* pmn = mnodeman.Find(voteVin.prevout);
1141  if (!pmn) {
1142  err = strprintf("unknown masternode - vin: %s", voteVin.prevout.ToString());
1143  // Ask for MN only if we finished syncing the MN list.
1144  if (pfrom && g_tiertwo_sync_state.IsMasternodeListSynced()) mnodeman.AskForMN(pfrom, voteVin);
1145  return state.DoS(0, false, REJECT_INVALID, "bad-mvote", false, err);
1146  }
1147 
1148  if (!pmn->IsEnabled()) {
1149  return state.DoS(0, false, REJECT_INVALID, "bad-mvote", false, "masternode not valid");
1150  }
1151 
1152  AddSeenProposalVote(vote);
1153 
1154  if (!vote.CheckSignature(pmn->pubKeyMasternode.GetID())) {
1156  err = strprintf("signature from masternode %s invalid", voteVin.prevout.ToString());
1157  return state.DoS(20, false, REJECT_INVALID, "bad-mvote-sig", false, err);
1158  }
1159  return false;
1160  }
1161 
1162  if (!UpdateProposal(vote, pfrom, err)) {
1163  return state.DoS(0, false, REJECT_INVALID, "bad-mvote", false, err);
1164  }
1165 
1166  // Relay only if we are synchronized
1167  // Makes no sense to relay votes to the peers from where we are syncing them.
1168  if (g_tiertwo_sync_state.IsSynced()) vote.Relay();
1170  LogPrint(BCLog::MNBUDGET, "mvote - new vote (%s) for proposal %s from dmn %s\n",
1171  voteID.ToString(), vote.GetProposalHash().ToString(), voteVin.prevout.ToString());
1172  return true;
1173 }
1174 
1176 {
1177 
1178  const uint256& nHash = finalbudget.GetHash();
1179  if (HaveFinalizedBudget(nHash)) {
1181  return 0;
1182  }
1183  if (!AddFinalizedBudget(finalbudget, pfrom)) {
1184  return 0;
1185  }
1186 
1187  // Relay only if we are synchronized
1188  // Makes no sense to relay finalizations to the peers from where we are syncing them.
1189  if (g_tiertwo_sync_state.IsSynced()) finalbudget.Relay();
1191 
1192  LogPrint(BCLog::MNBUDGET, "fbs (new) %s\n", nHash.ToString());
1193  //we might have active votes for this budget that are now valid
1194  CheckOrphanVotes();
1195  return 0;
1196 }
1197 
1199 {
1200  const uint256& voteID = vote.GetHash();
1201 
1202  if (HaveSeenFinalizedBudgetVote(voteID)) {
1204  return false;
1205  }
1206 
1207  std::string err;
1208  if (vote.GetTime() > GetTime() + (60 * 60)) {
1209  err = strprintf("new vote is too far ahead of current time - %s - nTime %lli - Max Time %lli\n",
1210  vote.GetHash().ToString(), vote.GetTime(), GetTime() + (60 * 60));
1211  return state.Invalid(false, REJECT_INVALID, "bad-fbvote", err);
1212  }
1213 
1214  const CTxIn& voteVin = vote.GetVin();
1215 
1216  // See if this vote was signed with a deterministic masternode
1217  auto mnList = deterministicMNManager->GetListAtChainTip();
1218  auto dmn = mnList.GetMNByCollateral(voteVin.prevout);
1219  if (dmn) {
1220  const std::string& mn_protx_id = dmn->proTxHash.ToString();
1221 
1222  if (dmn->IsPoSeBanned()) {
1223  err = strprintf("masternode (%s) not valid or PoSe banned", mn_protx_id);
1224  return state.DoS(0, false, REJECT_INVALID, "bad-fbvote", false, err);
1225  }
1226 
1228 
1229  if (!vote.CheckSignature(dmn->pdmnState->pubKeyOperator.Get())) {
1230  err = strprintf("invalid fbvote sig from dmn: %s", mn_protx_id);
1231  return state.DoS(100, false, REJECT_INVALID, "bad-fbvote-sig", false, err);
1232  }
1233 
1234  if (!UpdateFinalizedBudget(vote, pfrom, err)) {
1235  return state.DoS(0, false, REJECT_INVALID, "bad-fbvote", false, strprintf("%s (%s)", err, mn_protx_id));
1236  }
1237 
1238  // Relay only if we are synchronized
1239  // Makes no sense to relay votes to the peers from where we are syncing them.
1240  if (g_tiertwo_sync_state.IsSynced()) vote.Relay();
1242  LogPrint(BCLog::MNBUDGET, "fbvote - new vote (%s) for budget %s from dmn %s\n",
1243  voteID.ToString(), vote.GetBudgetHash().ToString(), mn_protx_id);
1244  return true;
1245  }
1246 
1247  // -- Legacy System (!TODO: remove after enforcement) --
1248  CMasternode* pmn = mnodeman.Find(voteVin.prevout);
1249  if (!pmn) {
1250  err = strprintf("unknown masternode - vin: %s", voteVin.prevout.ToString());
1251  // Ask for MN only if we finished syncing the MN list.
1252  if (pfrom && g_tiertwo_sync_state.IsMasternodeListSynced()) mnodeman.AskForMN(pfrom, voteVin);
1253  return state.DoS(0, false, REJECT_INVALID, "bad-fbvote", false, err);
1254  }
1255 
1256  if (!pmn->IsEnabled()) {
1257  return state.DoS(0, false, REJECT_INVALID, "bad-fbvote", false, "masternode not valid");
1258  }
1259 
1261 
1262  if (!vote.CheckSignature(pmn->pubKeyMasternode.GetID())) {
1264  err = strprintf("signature from masternode %s invalid", voteVin.prevout.ToString());
1265  return state.DoS(20, false, REJECT_INVALID, "bad-fbvote-sig", false, err);
1266  }
1267  return false;
1268  }
1269 
1270  if (!UpdateFinalizedBudget(vote, pfrom, err)) {
1271  return state.DoS(0, false, REJECT_INVALID, "bad-fbvote", false, err);
1272  }
1273 
1274  // Relay only if we are synchronized
1275  // Makes no sense to relay votes to the peers from where we are syncing them.
1276  if (g_tiertwo_sync_state.IsSynced()) vote.Relay();
1278  LogPrint(BCLog::MNBUDGET, "fbvote - new vote (%s) for budget %s from mn %s\n",
1279  voteID.ToString(), vote.GetBudgetHash().ToString(), voteVin.prevout.ToString());
1280  return true;
1281 }
1282 
1283 bool CBudgetManager::ProcessMessage(CNode* pfrom, std::string& strCommand, CDataStream& vRecv, int& banScore)
1284 {
1285  banScore = ProcessMessageInner(pfrom, strCommand, vRecv);
1286  return banScore == 0;
1287 }
1288 
1289 int CBudgetManager::ProcessMessageInner(CNode* pfrom, std::string& strCommand, CDataStream& vRecv)
1290 {
1291  if (!g_tiertwo_sync_state.IsBlockchainSynced()) return 0;
1292 
1293  if (strCommand == NetMsgType::BUDGETVOTESYNC) {
1294  // Masternode vote sync
1295  uint256 nProp;
1296  vRecv >> nProp;
1297  return ProcessBudgetVoteSync(nProp, pfrom);
1298  }
1299 
1300  if (strCommand == NetMsgType::BUDGETPROPOSAL) {
1301  // Masternode Proposal
1302  CBudgetProposal proposal;
1303  if (!proposal.ParseBroadcast(vRecv)) {
1304  return 20;
1305  }
1306  {
1307  // Clear inv request
1308  LOCK(cs_main);
1309  g_connman->RemoveAskFor(proposal.GetHash(), MSG_BUDGET_PROPOSAL);
1310  }
1311  return ProcessProposal(proposal);
1312  }
1313 
1314  if (strCommand == NetMsgType::BUDGETVOTE) {
1315  CBudgetVote vote;
1316  vRecv >> vote;
1317  vote.SetValid(true);
1318 
1319  {
1320  // Clear inv request
1321  LOCK(cs_main);
1322  g_connman->RemoveAskFor(vote.GetHash(), MSG_BUDGET_VOTE);
1323  }
1324 
1325  CValidationState state;
1326  if (!ProcessProposalVote(vote, pfrom, state)) {
1327  int nDos = 0;
1328  if (state.IsInvalid(nDos)) {
1329  LogPrint(BCLog::MNBUDGET, "%s: %s\n", __func__, FormatStateMessage(state));
1330  }
1331  return nDos;
1332  }
1333  return 0;
1334  }
1335 
1336  if (strCommand == NetMsgType::FINALBUDGET) {
1337  // Finalized Budget Suggestion
1338  CFinalizedBudget finalbudget;
1339  if (!finalbudget.ParseBroadcast(vRecv)) {
1340  return 20;
1341  }
1342  {
1343  // Clear inv request
1344  LOCK(cs_main);
1345  g_connman->RemoveAskFor(finalbudget.GetHash(), MSG_BUDGET_FINALIZED);
1346  }
1347  return ProcessFinalizedBudget(finalbudget, pfrom);
1348  }
1349 
1350  if (strCommand == NetMsgType::FINALBUDGETVOTE) {
1351  CFinalizedBudgetVote vote;
1352  vRecv >> vote;
1353  vote.SetValid(true);
1354 
1355  {
1356  // Clear inv request
1357  LOCK(cs_main);
1358  g_connman->RemoveAskFor(vote.GetHash(), MSG_BUDGET_FINALIZED_VOTE);
1359  }
1360 
1361  CValidationState state;
1362  if (!ProcessFinalizedBudgetVote(vote, pfrom, state)) {
1363  int nDos = 0;
1364  if (state.IsInvalid(nDos)) {
1365  LogPrint(BCLog::MNBUDGET, "%s: %s\n", __func__, FormatStateMessage(state));
1366  }
1367  return nDos;
1368  }
1369  return 0;
1370  }
1371 
1372  // nothing was done
1373  return 0;
1374 }
1375 
1376 void CBudgetManager::SetSynced(bool synced)
1377 {
1378  {
1379  LOCK(cs_proposals);
1380  for (auto& it: mapProposals) {
1381  CBudgetProposal* pbudgetProposal = &(it.second);
1382  if (pbudgetProposal && pbudgetProposal->IsValid()) {
1383  //mark votes
1384  pbudgetProposal->SetSynced(synced);
1385  }
1386  }
1387  }
1388  {
1389  LOCK(cs_budgets);
1390  for (auto& it: mapFinalizedBudgets) {
1391  CFinalizedBudget* pfinalizedBudget = &(it.second);
1392  if (pfinalizedBudget && pfinalizedBudget->IsValid()) {
1393  //mark votes
1394  pfinalizedBudget->SetSynced(synced);
1395  }
1396  }
1397  }
1398 }
1399 
1400 template<typename T>
1401 static bool relayItemIfFound(const uint256& itemHash, CNode* pfrom, RecursiveMutex& cs, std::map<uint256, T>& map, const char* type)
1402 {
1403  CNetMsgMaker msgMaker(pfrom->GetSendVersion());
1404  LOCK(cs);
1405  const auto& it = map.find(itemHash);
1406  if (it == map.end()) return false;
1407  T* item = &(it->second);
1408  if (!item->IsValid()) return true; // don't broadcast invalid items
1409  g_connman->PushMessage(pfrom, msgMaker.Make(type, item->GetBroadcast()));
1410  int nInvCount = 1;
1411  item->SyncVotes(pfrom, false /* fPartial */, nInvCount);
1412  LogPrint(BCLog::MNBUDGET, "%s: single %s sent %d items\n", __func__, type, nInvCount);
1413  return true;
1414 }
1415 
1416 template<typename T>
1417 static void relayInventoryItems(CNode* pfrom, RecursiveMutex& cs, std::map<uint256, T>& map, bool fPartial, GetDataMsg invType, const int mn_sync_budget_type)
1418 {
1419  CNetMsgMaker msgMaker(pfrom->GetSendVersion());
1420  int nInvCount = 0;
1421  {
1422  LOCK(cs);
1423  for (auto& it: map) {
1424  T* item = &(it.second);
1425  if (item && item->IsValid()) {
1426  pfrom->PushInventory(CInv(invType, item->GetHash()));
1427  nInvCount++;
1428  item->SyncVotes(pfrom, fPartial, nInvCount);
1429  }
1430  }
1431  }
1432  g_connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::SYNCSTATUSCOUNT, mn_sync_budget_type, nInvCount));
1433  LogPrint(BCLog::MNBUDGET, "%s: sent %d items\n", __func__, nInvCount);
1434 }
1435 
1437 {
1438  if (nProp.IsNull()) return;
1439  // Try first to relay a proposal
1440  if (relayItemIfFound<CBudgetProposal>(nProp, pfrom, cs_proposals, mapProposals, NetMsgType::BUDGETPROPOSAL)) {
1441  return;
1442  }
1443  // Try now to relay a finalization
1444  if (relayItemIfFound<CFinalizedBudget>(nProp, pfrom, cs_budgets, mapFinalizedBudgets, NetMsgType::FINALBUDGET)) {
1445  return;
1446  }
1447  LogPrint(BCLog::MNBUDGET, "%s: single request budget item not found\n", __func__);
1448 }
1449 
1450 
1451 void CBudgetManager::Sync(CNode* pfrom, bool fPartial)
1452 {
1453  // Full budget sync request.
1454  relayInventoryItems<CBudgetProposal>(pfrom, cs_proposals, mapProposals, fPartial, MSG_BUDGET_PROPOSAL, MASTERNODE_SYNC_BUDGET_PROP);
1455  relayInventoryItems<CFinalizedBudget>(pfrom, cs_budgets, mapFinalizedBudgets, fPartial, MSG_BUDGET_FINALIZED, MASTERNODE_SYNC_BUDGET_FIN);
1456 
1457  if (!fPartial) {
1458  // We are not going to answer full budget sync requests for an hour (chainparams.FulfilledRequestExpireTime()).
1459  // The remote peer can still do single prop and mnv sync requests if needed.
1460  g_netfulfilledman.AddFulfilledRequest(pfrom->addr, BUDGET_SYNC_REQUEST_RECV);
1461  }
1462 }
1463 
1464 template<typename T>
1465 static void TryAppendOrphanVoteMap(const T& vote,
1466  const uint256& parentHash,
1467  std::map<uint256, std::pair<std::vector<T>, int64_t>>& mapOrphan,
1468  std::map<uint256, T>& mapSeen)
1469 {
1470  if (mapOrphan.size() > ORPHAN_VOTES_CACHE_LIMIT) {
1471  // future: notify user about this
1472  mapSeen.erase(vote.GetHash());
1473  } else {
1474  // Append orphan vote
1475  const auto& it = mapOrphan.find(parentHash);
1476  if (it != mapOrphan.end()) {
1477  // Check size limit and erase it from the seen map if we already passed it
1478  if (it->second.first.size() > ORPHAN_VOTES_CACHE_LIMIT) {
1479  // future: check if the MN already voted and replace vote
1480  mapSeen.erase(vote.GetHash());
1481  } else {
1482  it->second.first.emplace_back(vote);
1483  it->second.second = GetTime();
1484  }
1485  } else {
1486  mapOrphan.emplace(parentHash, std::make_pair<std::vector<T>, int64_t>({vote}, GetTime()));
1487  }
1488  }
1489 }
1490 
1491 bool CBudgetManager::UpdateProposal(const CBudgetVote& vote, CNode* pfrom, std::string& strError)
1492 {
1493  LOCK(cs_proposals);
1494 
1495  const uint256& nProposalHash = vote.GetProposalHash();
1496  const auto& itProposal = mapProposals.find(nProposalHash);
1497  if (itProposal == mapProposals.end()) {
1498  if (pfrom) {
1499  // only ask for missing items after our syncing process is complete --
1500  // otherwise we'll think a full sync succeeded when they return a result
1501  if (!g_tiertwo_sync_state.IsSynced()) return false;
1502 
1503  LogPrint(BCLog::MNBUDGET,"%s: Unknown proposal %d, asking for source proposal\n", __func__, nProposalHash.ToString());
1504  {
1505  LOCK(cs_votes);
1506  TryAppendOrphanVoteMap<CBudgetVote>(vote, nProposalHash, mapOrphanProposalVotes, mapSeenProposalVotes);
1507  }
1508 
1509  if (!g_netfulfilledman.HasItemRequest(pfrom->addr, nProposalHash)) {
1510  g_connman->PushMessage(pfrom, CNetMsgMaker(pfrom->GetSendVersion()).Make(NetMsgType::BUDGETVOTESYNC, nProposalHash));
1511  g_netfulfilledman.AddItemRequest(pfrom->addr, nProposalHash);
1512  }
1513  }
1514 
1515  strError = "Proposal not found!";
1516  return false;
1517  }
1518 
1519  // Add or update vote
1520  return itProposal->second.AddOrUpdateVote(vote, strError);
1521 }
1522 
1523 bool CBudgetManager::UpdateFinalizedBudget(const CFinalizedBudgetVote& vote, CNode* pfrom, std::string& strError)
1524 {
1525  LOCK(cs_budgets);
1526 
1527  const uint256& nBudgetHash = vote.GetBudgetHash();
1528  if (!mapFinalizedBudgets.count(nBudgetHash)) {
1529  if (pfrom) {
1530  // only ask for missing items after our syncing process is complete --
1531  // otherwise we'll think a full sync succeeded when they return a result
1532  if (!g_tiertwo_sync_state.IsSynced()) return false;
1533 
1534  LogPrint(BCLog::MNBUDGET,"%s: Unknown Finalized Proposal %s, asking for source budget\n", __func__, nBudgetHash.ToString());
1535  {
1537  TryAppendOrphanVoteMap<CFinalizedBudgetVote>(vote, nBudgetHash, mapOrphanFinalizedBudgetVotes, mapSeenFinalizedBudgetVotes);
1538  }
1539 
1540  if (!g_netfulfilledman.HasItemRequest(pfrom->addr, nBudgetHash)) {
1541  g_connman->PushMessage(pfrom, CNetMsgMaker(pfrom->GetSendVersion()).Make(NetMsgType::BUDGETVOTESYNC, nBudgetHash));
1542  g_netfulfilledman.AddItemRequest(pfrom->addr, nBudgetHash);
1543  }
1544  }
1545 
1546  strError = "Finalized Budget " + nBudgetHash.ToString() + " not found!";
1547  return false;
1548  }
1549  LogPrint(BCLog::MNBUDGET,"%s: Finalized Proposal %s added\n", __func__, nBudgetHash.ToString());
1550  return mapFinalizedBudgets[nBudgetHash].AddOrUpdateVote(vote, strError);
1551 }
1552 
1553 std::string CBudgetManager::ToString() const
1554 {
1555  unsigned int nProposals = WITH_LOCK(cs_proposals, return mapProposals.size(); );
1556  unsigned int nBudgets = WITH_LOCK(cs_budgets, return mapFinalizedBudgets.size(); );
1557 
1558  unsigned int nSeenVotes = 0, nOrphanVotes = 0;
1559  {
1560  LOCK(cs_votes);
1561  nSeenVotes = mapSeenProposalVotes.size();
1562  nOrphanVotes = mapOrphanProposalVotes.size();
1563  }
1564 
1565  unsigned int nSeenFinalizedVotes = 0, nOrphanFinalizedVotes = 0;
1566  {
1568  nSeenFinalizedVotes = mapSeenFinalizedBudgetVotes.size();
1569  nOrphanFinalizedVotes = mapOrphanFinalizedBudgetVotes.size();
1570  }
1571 
1572  return strprintf("Proposals: %d - Finalized Budgets: %d - "
1573  "Proposal Votes: %d (orphan: %d) - "
1574  "Finalized Budget Votes: %d (orphan: %d)",
1575  nProposals, nBudgets,
1576  nSeenVotes, nOrphanVotes, nSeenFinalizedVotes, nOrphanFinalizedVotes);
1577 }
1578 
1579 
1580 /*
1581  * Check Collateral
1582  */
1583 bool CheckCollateralConfs(const uint256& nTxCollateralHash, int nCurrentHeight, int nProposalHeight, std::string& strError)
1584 {
1585  const int nRequiredConfs = Params().GetConsensus().nBudgetFeeConfirmations;
1586  const int nConf = nCurrentHeight - nProposalHeight + 1;
1587 
1588  if (nConf < nRequiredConfs) {
1589  strError = strprintf("Collateral requires at least %d confirmations - %d confirmations (current height: %d, fee tx height: %d)",
1590  nRequiredConfs, nConf, nCurrentHeight, nProposalHeight);
1591  LogPrint(BCLog::MNBUDGET,"%s: %s\n", __func__, strError);
1592  return false;
1593  }
1594  return true;
1595 }
1596 
1597 bool CheckCollateral(const uint256& nTxCollateralHash, const uint256& nExpectedHash, std::string& strError, int64_t& nTime, int nCurrentHeight, bool fBudgetFinalization)
1598 {
1599  CTransactionRef txCollateral;
1600  uint256 nBlockHash;
1601  if (!GetTransaction(nTxCollateralHash, txCollateral, nBlockHash, true)) {
1602  strError = strprintf("Can't find collateral tx %s", nTxCollateralHash.ToString());
1603  return false;
1604  }
1605 
1606  if (txCollateral->vout.size() < 1) return false;
1607  if (txCollateral->nLockTime != 0) return false;
1608 
1609  CScript findScript;
1610  findScript << OP_RETURN << ToByteVector(nExpectedHash);
1611 
1612  bool foundOpReturn = false;
1613  for (const CTxOut &o : txCollateral->vout) {
1615  strError = strprintf("Invalid Script %s", txCollateral->ToString());
1616  return false;
1617  }
1618  if (fBudgetFinalization) {
1619  // Collateral for budget finalization
1620  // Note: there are still old valid budgets out there, but the check for the new 5 PIV finalization collateral
1621  // will also cover the old 50 PIV finalization collateral.
1622  LogPrint(BCLog::MNBUDGET, "Final Budget: o.scriptPubKey(%s) == findScript(%s) ?\n", HexStr(o.scriptPubKey), HexStr(findScript));
1623  if (o.scriptPubKey == findScript) {
1624  LogPrint(BCLog::MNBUDGET, "Final Budget: o.nValue(%ld) >= BUDGET_FEE_TX(%ld) ?\n", o.nValue, BUDGET_FEE_TX);
1625  if(o.nValue >= BUDGET_FEE_TX) {
1626  foundOpReturn = true;
1627  break;
1628  }
1629  }
1630  } else {
1631  // Collateral for normal budget proposal
1632  LogPrint(BCLog::MNBUDGET, "Normal Budget: o.scriptPubKey(%s) == findScript(%s) ?\n", HexStr(o.scriptPubKey), HexStr(findScript));
1633  if (o.scriptPubKey == findScript) {
1634  LogPrint(BCLog::MNBUDGET, "Normal Budget: o.nValue(%ld) >= PROPOSAL_FEE_TX(%ld) ?\n", o.nValue, PROPOSAL_FEE_TX);
1635  if(o.nValue >= PROPOSAL_FEE_TX) {
1636  foundOpReturn = true;
1637  break;
1638  }
1639  }
1640  }
1641  }
1642 
1643  if (!foundOpReturn) {
1644  strError = strprintf("Couldn't find opReturn %s in %s", nExpectedHash.ToString(), txCollateral->ToString());
1645  return false;
1646  }
1647 
1648  // Retrieve block height (checking that it's in the active chain) and time
1649  // both get set in CBudgetProposal/CFinalizedBudget by the caller (AddProposal/AddFinalizedBudget)
1650  if (nBlockHash.IsNull()) {
1651  strError = strprintf("Collateral transaction %s is unconfirmed", nTxCollateralHash.ToString());
1652  return false;
1653  }
1654  nTime = 0;
1655  int nProposalHeight = 0;
1656  {
1657  LOCK(cs_main);
1658  CBlockIndex* pindex = LookupBlockIndex(nBlockHash);
1659  if (pindex && chainActive.Contains(pindex)) {
1660  nProposalHeight = pindex->nHeight;
1661  nTime = pindex->nTime;
1662  }
1663  }
1664 
1665  if (!nProposalHeight) {
1666  strError = strprintf("Collateral transaction %s not in Active chain", nTxCollateralHash.ToString());
1667  return false;
1668  }
1669 
1670  return CheckCollateralConfs(nTxCollateralHash, nCurrentHeight, nProposalHeight, strError);
1671 }
bool GetActiveMasternodeKeys(CTxIn &vin, Optional< CKey > &key, CBLSSecretKey &blsKey)
int64_t CAmount
Amount in PIV (Can be negative)
Definition: amount.h:13
CBudgetManager g_budgetman
#define BUDGET_ORPHAN_VOTES_CLEANUP_SECONDS
bool CheckCollateralConfs(const uint256 &nTxCollateralHash, int nCurrentHeight, int nProposalHeight, std::string &strError)
bool CheckCollateral(const uint256 &nTxCollateralHash, const uint256 &nExpectedHash, std::string &strError, int64_t &nTime, int nCurrentHeight, bool fBudgetFinalization)
#define ORPHAN_VOTES_CACHE_LIMIT
Definition: budgetmanager.h:15
const CChainParams & Params()
Return the currently selected parameters.
void reserve(size_type n)
Definition: streams.h:168
The block chain is a tree shaped structure starting with the genesis block at the root,...
Definition: chain.h:139
uint32_t nTime
Definition: chain.h:196
int nHeight
height of the entry in the chain. The genesis block has height 0
Definition: chain.h:151
int GetBestHeight() const
bool AddProposal(CBudgetProposal &budgetProposal)
int GetHighestVoteCount(int chainHeight) const
std::map< uint256, BudVotesAndLastVoteReceivedTime > mapOrphanFinalizedBudgetVotes
Definition: budgetmanager.h:40
std::string strBudgetMode
Definition: budgetmanager.h:66
bool ProcessProposalVote(CBudgetVote &proposal, CNode *pfrom, CValidationState &state)
bool HaveSeenProposalVote(const uint256 &voteHash) const
Definition: budgetmanager.h:73
bool HaveProposal(const uint256 &propHash) const
Definition: budgetmanager.h:72
void SyncSingleItem(CNode *pfrom, const uint256 &nProp)
void SetBudgetProposalsStr(CFinalizedBudget &finalizedBudget) const
void SetSynced(bool synced)
HighestFinBudget GetBudgetWithHighestVoteCount(int chainHeight) const
bool UpdateProposal(const CBudgetVote &vote, CNode *pfrom, std::string &strError)
TrxValidationStatus IsTransactionValid(const CTransaction &txNew, const uint256 &nBlockHash, int nBlockHeight) const
bool GetProposal(const uint256 &nHash, CBudgetProposal &bp) const
RecursiveMutex cs_finalizedvotes
Definition: budgetmanager.h:62
CDataStream GetProposalVoteSerialized(const uint256 &voteHash) const
static CAmount GetTotalBudget(int nHeight)
std::map< uint256, uint256 > mapUnconfirmedFeeTx
Definition: budgetmanager.h:25
std::map< uint256, uint256 > mapFeeTxToBudget
Definition: budgetmanager.h:30
bool FillBlockPayee(CMutableTransaction &txCoinbase, CMutableTransaction &txCoinstake, const int nHeight, bool fProofOfStake) const
bool GetPayeeAndAmount(int chainHeight, CScript &payeeRet, CAmount &nAmountRet) const
bool AddFinalizedBudget(CFinalizedBudget &finalizedBudget, CNode *pfrom=nullptr)
bool GetFinalizedBudget(const uint256 &nHash, CFinalizedBudget &fb) const
bool ProcessMessage(CNode *pfrom, std::string &strCommand, CDataStream &vRecv, int &banScore)
bool HaveFinalizedBudget(const uint256 &budgetHash) const
Definition: budgetmanager.h:74
std::string ToString() const
std::map< uint256, CFinalizedBudget > mapFinalizedBudgets
Definition: budgetmanager.h:33
std::map< uint256, CBudgetVote > mapSeenProposalVotes
Definition: budgetmanager.h:35
void CheckOrphanVotes()
CBudgetProposal * FindProposal(const uint256 &nHash)
RecursiveMutex cs_budgets
Definition: budgetmanager.h:60
std::map< uint256, CBudgetProposal > mapProposals
Definition: budgetmanager.h:32
int ProcessProposal(CBudgetProposal &proposal)
std::vector< CBudgetProposal > GetBudget()
const CBudgetProposal * FindProposalByName(const std::string &strProposalName) const
bool ProcessFinalizedBudgetVote(CFinalizedBudgetVote &vote, CNode *pfrom, CValidationState &state)
RecursiveMutex cs_proposals
Definition: budgetmanager.h:61
CDataStream GetProposalSerialized(const uint256 &propHash) const
uint256 SubmitFinalBudget()
std::map< uint256, PropVotesAndLastVoteReceivedTime > mapOrphanProposalVotes
Definition: budgetmanager.h:37
RecursiveMutex cs_votes
Definition: budgetmanager.h:63
CDataStream GetFinalizedBudgetSerialized(const uint256 &budgetHash) const
std::map< uint256, uint256 > mapFeeTxToProposal
Definition: budgetmanager.h:29
void VoteOnFinalizedBudgets()
std::string GetRequiredPaymentsString(int nBlockHeight)
std::vector< CBudgetProposal * > GetAllProposalsOrdered()
bool GetExpectedPayeeAmount(int chainHeight, CAmount &nAmountRet) const
CDataStream GetFinalizedBudgetVoteSerialized(const uint256 &voteHash) const
CFinalizedBudget * FindFinalizedBudget(const uint256 &nHash)
std::vector< CFinalizedBudget * > GetFinalizedBudgets()
void RemoveByFeeTxId(const uint256 &feeTxId)
void RemoveStaleVotesOnFinalBudget(CFinalizedBudget *fbud)
void UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) override
Notifies listeners when the block chain tip advances.
bool HaveSeenFinalizedBudgetVote(const uint256 &voteHash) const
Definition: budgetmanager.h:75
int ProcessFinalizedBudget(CFinalizedBudget &finalbudget, CNode *pfrom)
int ProcessBudgetVoteSync(const uint256 &nProp, CNode *pfrom)
int ProcessMessageInner(CNode *pfrom, std::string &strCommand, CDataStream &vRecv)
Process the message and returns the ban score (0 if no banning is needed)
void Sync(CNode *node, bool fPartial)
void AddSeenFinalizedBudgetVote(const CFinalizedBudgetVote &vote)
bool IsBudgetPaymentBlock(int nBlockHeight) const
std::string GetFinalizedBudgetStatus(const uint256 &nHash) const
std::map< uint256, CFinalizedBudgetVote > mapSeenFinalizedBudgetVotes
Definition: budgetmanager.h:38
void ForceAddFinalizedBudget(const uint256 &nHash, const uint256 &feeTxId, const CFinalizedBudget &finalizedBudget)
void AddSeenProposalVote(const CBudgetVote &vote)
bool AddAndRelayProposalVote(const CBudgetVote &vote, std::string &strError)
bool UpdateFinalizedBudget(const CFinalizedBudgetVote &vote, CNode *pfrom, std::string &strError)
void RemoveStaleVotesOnProposal(CBudgetProposal *prop)
int GetYeas() const
std::map< COutPoint, CBudgetVote > mapVotes
int GetBlockStart() const
std::map< COutPoint, CBudgetVote > GetVotes() const
std::string GetName() const
const uint256 & GetFeeTXHash() const
CScript GetPayee() const
bool IsValid() const
bool ParseBroadcast(CDataStream &broadcast)
void SetSynced(bool synced)
void SetStrInvalid(const std::string &_strInvalid)
bool AddOrUpdateVote(const CBudgetVote &vote, std::string &strError)
static bool PtrHigherYes(CBudgetProposal *a, CBudgetProposal *b)
CAmount GetAmount() const
bool UpdateValid(int nHeight, int mnCount)
bool IsWellFormed(const CAmount &nTotalBudget)
int GetNays() const
std::string IsInvalidLogStr() const
uint256 GetHash() const
CTxIn GetVin() const
Definition: budgetvote.h:54
uint256 GetProposalHash() const
Definition: budgetvote.h:59
int64_t GetTime() const
Definition: budgetvote.h:60
void Relay() const
Definition: budgetvote.cpp:32
uint256 GetHash() const
Definition: budgetvote.cpp:38
void SetValid(bool _fValid)
Definition: budgetvote.h:66
bool Contains(const CBlockIndex *pindex) const
Efficiently check whether a block is present in this chain.
Definition: chain.h:435
const Consensus::Params & GetConsensus() const
Definition: chainparams.h:72
bool IsTestnet() const
Definition: chainparams.h:99
bool GetPayeeAndAmount(int64_t nBlockHeight, CScript &payee, CAmount &nAmount) const
std::string IsInvalidLogStr() const
void SetAutoChecked(bool _fAutoChecked)
static bool PtrGreater(CFinalizedBudget *a, CFinalizedBudget *b)
std::string GetProposalsStr() const
bool GetBudgetPaymentByBlock(int64_t nBlockHeight, CTxBudgetPayment &payment) const
bool UpdateValid(int nHeight)
bool IsValid() const
std::string GetName() const
void SetProposalsStr(const std::string _strProposals)
void SetSynced(bool synced)
const uint256 & GetFeeTXHash() const
bool AddOrUpdateVote(const CFinalizedBudgetVote &vote, std::string &strError)
TrxValidationStatus IsTransactionValid(const CTransaction &txNew, const uint256 &nBlockHash, int nBlockHeight) const
bool ParseBroadcast(CDataStream &broadcast)
bool CheckProposals(const std::map< uint256, CBudgetProposal > &mapWinningProposals) const
std::map< COutPoint, CFinalizedBudgetVote > mapVotes
void SetStrInvalid(const std::string &_strInvalid)
bool IsWellFormed(const CAmount &nTotalBudget)
int GetBlockEnd() const
bool IsAutoChecked() const
std::vector< uint256 > GetVotesHashes() const
std::vector< uint256 > GetProposalsHashes() const
uint256 GetHash() const
int GetVoteCount() const
int GetBlockStart() const
uint256 GetBudgetHash() const
void SetValid(bool _fValid)
inv message data
Definition: protocol.h:466
CPubKey pubKeyMasternode
Definition: masternode.h:99
bool IsEnabled() const
Definition: masternode.h:193
int CountEnabled(bool only_legacy=false) const
void AskForMN(CNode *pnode, const CTxIn &vin)
Ask (source) node for mnb.
CMasternode * Find(const COutPoint &collateralOut)
Find an entry.
bool IsRFC1918() const
Definition: netaddress.cpp:299
bool IsLocal() const
Definition: netaddress.cpp:402
void AddFulfilledRequest(const CService &addr, const std::string &strRequest)
void AddItemRequest(const CService &addr, const uint256 &itemHash)
bool HasFulfilledRequest(const CService &addr, const std::string &strRequest) const
bool HasItemRequest(const CService &addr, const uint256 &itemHash) const
CSerializedNetMsg Make(int nFlags, std::string sCommand, Args &&... args)
Information about a peer.
Definition: net.h:669
std::atomic< int > nVersion
Definition: net.h:699
NodeId GetId() const
Definition: net.h:825
int GetSendVersion() const
Definition: net.cpp:789
const CAddress addr
Definition: net.h:698
void PushInventory(const CInv &inv)
Definition: net.h:914
std::string ToString() const
Definition: transaction.cpp:18
CKeyID GetID() const
Get the KeyID of this public key (hash of its serialization)
Definition: pubkey.h:167
A key allocated from the key pool.
Definition: wallet.h:1256
Serialized script, used inside transaction inputs and outputs.
Definition: script.h:381
bool IsUnspendable() const
Returns whether the script is guaranteed to fail at execution, regardless of the initial stack.
Definition: script.h:654
bool IsPayToPublicKeyHash() const
Definition: script.cpp:212
bool Sign(const CKey &key, const CKeyID &keyID)
CSignedMessage Class Functions inherited by network signed-messages.
bool CheckSignature(const CKeyID &keyID) const
The basic transaction that is broadcasted on the network and contained in blocks.
Definition: transaction.h:244
An input of a transaction.
Definition: transaction.h:94
COutPoint prevout
Definition: transaction.h:96
An output of a transaction.
Definition: transaction.h:137
CScript scriptPubKey
Definition: transaction.h:140
CAmount nValue
Definition: transaction.h:139
Capture information about block/transaction validation.
Definition: validation.h:24
bool Invalid(bool ret=false, unsigned int _chRejectCode=0, const std::string &_strRejectReason="", const std::string &_strDebugMessage="")
Definition: validation.h:55
bool DoS(int level, bool ret=false, unsigned int chRejectCodeIn=0, std::string strRejectReasonIn="", bool corruptionIn=false, const std::string &strDebugMessageIn="")
Definition: validation.h:39
bool IsInvalid() const
Definition: validation.h:73
bool IsBlockchainSynced() const
bool IsSynced() const
bool IsMasternodeListSynced() const
int GetSyncPhase() const
void AddedBudgetItem(const uint256 &hash)
std::string ToString() const
Definition: uint256.cpp:65
bool IsNull() const
Definition: uint256.h:36
256-bit opaque blob.
Definition: uint256.h:138
std::unique_ptr< CDeterministicMNManager > deterministicMNManager
TrxValidationStatus
@ Valid
Transaction verification failed.
@ VoteThreshold
Transaction successfully verified, but includes a double-budget-payment.
#define T(expected, seed, data)
std::unique_ptr< CConnman > g_connman
Definition: init.cpp:90
@ LOCK
Definition: lockunlock.h:16
#define LogPrint(category,...)
Definition: logging.h:163
CMasternodeMan mnodeman
Masternode manager.
@ MNBUDGET
Definition: logging.h:60
@ MASTERNODE
Definition: logging.h:59
@ UPGRADE_V6_0
Definition: params.h:41
@ UPGRADE_V5_5
Definition: params.h:39
const char * BUDGETVOTESYNC
The budgetvotesync message is used to request budget vote data from connected peers.
Definition: protocol.cpp:51
const char * FINALBUDGETVOTE
The finalbudgetvote message is used to broadcast or relay finalized budget votes to connected peers.
Definition: protocol.cpp:53
const char * FINALBUDGET
The finalbudget message is used to broadcast or relay finalized budget metadata to connected peers.
Definition: protocol.cpp:52
const char * BUDGETVOTE
The budgetvote message is used to broadcast or relay budget proposal votes to connected peers.
Definition: protocol.cpp:50
const char * SYNCSTATUSCOUNT
The syncstatuscount message is used to track the layer 2 syncing process.
Definition: protocol.cpp:54
const char * BUDGETPROPOSAL
The budgetproposal message is used to broadcast or relay budget proposal metadata to connected peers.
Definition: protocol.cpp:49
std::string EncodeDestination(const CWDestination &address, const CChainParams::Base58Type addrType)
RecursiveMutex cs_main
Global state.
Definition: validation.cpp:80
CNetFulfilledRequestManager g_netfulfilledman(DEFAULT_ITEMS_FILTER_SIZE)
boost::optional< T > Optional
Substitute for C++17 std::optional.
Definition: optional.h:12
GetDataMsg
getdata message types
Definition: protocol.h:434
@ MSG_BUDGET_VOTE
Definition: protocol.h:446
@ MSG_BUDGET_PROPOSAL
Definition: protocol.h:447
@ MSG_BUDGET_FINALIZED_VOTE
Definition: protocol.h:449
@ MSG_BUDGET_FINALIZED
Definition: protocol.h:448
int GetRandInt(int nMax) noexcept
Definition: random.cpp:591
std::vector< unsigned char > ToByteVector(const T &in)
Definition: script.h:43
@ OP_RETURN
Definition: script.h:87
@ SER_NETWORK
Definition: serialize.h:174
bool ExtractDestination(const CScript &scriptPubKey, CTxDestination &addressRet, bool fColdStake)
Parse a standard scriptPubKey for the destination address.
Definition: standard.cpp:162
boost::variant< CNoDestination, CKeyID, CScriptID, CExchangeKeyID > CTxDestination
A txout script template with a specific destination.
Definition: standard.h:72
A mutable version of CTransaction.
Definition: transaction.h:409
std::vector< CTxOut > vout
Definition: transaction.h:411
CWallet::CommitStatus status
Definition: wallet.h:1106
int nBudgetFeeConfirmations
Definition: params.h:179
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
int nBudgetCycleBlocks
Definition: params.h:178
#define AssertLockNotHeld(cs)
Definition: sync.h:76
#define LOCK2(cs1, cs2)
Definition: sync.h:221
#define WITH_LOCK(cs, code)
Run code while locking a mutex.
Definition: sync.h:247
#define AssertLockHeld(cs)
Definition: sync.h:75
std::atomic< bool > fMasterNode
Definition: system.cpp:87
TierTwoSyncState g_tiertwo_sync_state
#define MASTERNODE_SYNC_BUDGET_PROP
#define MASTERNODE_SYNC_BUDGET
#define MASTERNODE_SYNC_BUDGET_FIN
#define strprintf
Definition: tinyformat.h:1056
std::shared_ptr< const CTransaction > CTransactionRef
Definition: transaction.h:456
const uint256 UINT256_ZERO
constant uint256 instances
Definition: uint256.h:175
std::string FormatStateMessage(const CValidationState &state)
Convert CValidationState to a human-readable message for logging.
Definition: validation.cpp:13
std::string HexStr(const Span< const uint8_t > s)
Convert a span of bytes to a lower-case hexadecimal string.
int64_t GetTime()
DEPRECATED Use either GetSystemTimeInSeconds (not mockable) or GetTime<T> (mockable)
Definition: utiltime.cpp:27
int ActiveProtocol()
See whether the protocol update is enforced for connected nodes.
bool GetTransaction(const uint256 &hash, CTransactionRef &txOut, uint256 &hashBlock, bool fAllowSlow, CBlockIndex *blockIndex)
Return transaction in tx, and if it was found inside a block, its hash is placed in hashBlock.
Definition: validation.cpp:671
CAmount GetBlockValue(int nHeight)
Definition: validation.cpp:816
CChain chainActive
The currently-connected chain of blocks (protected by cs_main).
Definition: validation.cpp:84
CBlockIndex * LookupBlockIndex(const uint256 &hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
Definition: validation.h:345
std::vector< CWalletRef > vpwallets
Definition: wallet.cpp:33