PIVX Core  5.6.99
P2P Digital Currency
budgetproposal.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 
7 #include "chainparams.h"
8 #include "script/standard.h"
9 #include "utilstrencodings.h"
10 
12  nAllotted(0),
13  fValid(true),
14  strInvalid(""),
15  strProposalName("unknown"),
16  strURL(""),
17  nBlockStart(0),
18  nBlockEnd(0),
19  address(),
20  nAmount(0),
21  nFeeTXHash(UINT256_ZERO),
22  nTime(0)
23 {}
24 
26  const std::string& url,
27  int paycount,
28  const CScript& payee,
29  const CAmount& amount,
30  int blockstart,
31  const uint256& nfeetxhash):
32  nAllotted(0),
33  fValid(true),
34  strInvalid(""),
35  strProposalName(name),
36  strURL(url),
37  nBlockStart(blockstart),
38  address(payee),
39  nAmount(amount),
40  nFeeTXHash(nfeetxhash),
41  nTime(0)
42 {
43  // calculate the expiration block
45 }
46 
47 // initialize from network broadcast message
49 {
50  *this = CBudgetProposal();
51  try {
52  broadcast >> LIMITED_STRING(strProposalName, 20);
53  broadcast >> LIMITED_STRING(strURL, 64);
54  broadcast >> nTime;
55  broadcast >> nBlockStart;
56  broadcast >> nBlockEnd;
57  broadcast >> nAmount;
58  broadcast >> address;
59  broadcast >> nFeeTXHash;
60  } catch (std::exception& e) {
61  return error("Unable to deserialize proposal broadcast: %s", e.what());
62  }
63  return true;
64 }
65 
66 void CBudgetProposal::SyncVotes(CNode* pfrom, bool fPartial, int& nInvCount) const
67 {
68  for (const auto& it: mapVotes) {
69  const CBudgetVote& vote = it.second;
70  if (vote.IsValid() && (!fPartial || !vote.IsSynced())) {
71  pfrom->PushInventory(CInv(MSG_BUDGET_VOTE, vote.GetHash()));
72  nInvCount++;
73  }
74  }
75 }
76 
78 {
79  if (GetNays() - GetYeas() > 3 * mnCount / 10) {
80  strInvalid = "Heavily Downvoted";
81  return true;
82  }
83  return false;
84 }
85 
87 {
88  // block start must be a superblock
89  if (nBlockStart < 0 ||
90  nBlockStart % Params().GetConsensus().nBudgetCycleBlocks != 0) {
91  strInvalid = "Invalid nBlockStart";
92  return false;
93  }
94 
95  if (nBlockEnd < nBlockStart) {
96  strInvalid = "Invalid nBlockEnd (end before start)";
97  return false;
98  }
99 
100  if (GetTotalPaymentCount() > Params().GetConsensus().nMaxProposalPayments) {
101  strInvalid = "Invalid payment count";
102  return false;
103  }
104 
105  return true;
106 }
107 
108 bool CBudgetProposal::CheckAmount(const CAmount& nTotalBudget)
109 {
110  // check minimum amount
111  if (nAmount < PROPOSAL_MIN_AMOUNT) {
112  strInvalid = "Invalid nAmount (too low)";
113  return false;
114  }
115 
116  // check maximum amount
117  // can only pay out 10% of the possible coins (min value of coins)
118  if (nAmount > nTotalBudget) {
119  strInvalid = "Invalid nAmount (too high)";
120  return false;
121  }
122 
123  return true;
124 }
125 
127 {
128  // !TODO: There might be an issue with multisig in the coinbase on mainnet
129  // we will add support for it in a future release.
130  if (address.IsPayToScriptHash()) {
131  strInvalid = "Multisig is not currently supported.";
132  return false;
133  }
134 
135  // Check address
136  CTxDestination dest;
137  if (!ExtractDestination(address, dest, false)) {
138  strInvalid = "Invalid script";
139  return false;
140  }
141  if (!IsValidDestination(dest)) {
142  strInvalid = "Invalid recipient address";
143  return false;
144  }
145 
146  return true;
147 }
148 
149 /* TODO: Add this to IsWellFormed() for the next hard-fork
150  * This will networkly reject malformed proposal names and URLs
151  */
153 {
155  strInvalid = "Proposal name contains illegal characters.";
156  return false;
157  }
158  if (strURL != SanitizeString(strURL)) {
159  strInvalid = "Proposal URL contains illegal characters.";
160  }
161 }
162 
163 bool CBudgetProposal::IsWellFormed(const CAmount& nTotalBudget)
164 {
165  return CheckStartEnd() && CheckAmount(nTotalBudget) && CheckAddress();
166 }
167 
168 bool CBudgetProposal::updateExpired(int nCurrentHeight)
169 {
170  if (IsExpired(nCurrentHeight)) {
171  strInvalid = "Proposal expired";
172  return true;
173  }
174  return false;
175 }
176 
177 bool CBudgetProposal::UpdateValid(int nCurrentHeight, int mnCount)
178 {
179  fValid = false;
180 
181  // Never kill a proposal before the first superblock
182  if (nCurrentHeight > nBlockStart && IsHeavilyDownvoted(mnCount)) {
183  return false;
184  }
185 
186  if (updateExpired(nCurrentHeight)) {
187  return false;
188  }
189 
190  fValid = true;
191  strInvalid.clear();
192  return true;
193 }
194 
196 {
198 }
199 
200 bool CBudgetProposal::IsPassing(int nBlockStartBudget, int nBlockEndBudget, int mnCount) const
201 {
202  if (!fValid)
203  return false;
204 
205  if (this->nBlockStart > nBlockStartBudget)
206  return false;
207 
208  if (this->nBlockEnd < nBlockEndBudget)
209  return false;
210 
211  if (GetYeas() - GetNays() <= mnCount / 10)
212  return false;
213 
214  if (!IsEstablished())
215  return false;
216 
217  return true;
218 }
219 
220 bool CBudgetProposal::IsExpired(int nCurrentHeight) const
221 {
222  return nBlockEnd < nCurrentHeight;
223 }
224 
225 bool CBudgetProposal::AddOrUpdateVote(const CBudgetVote& vote, std::string& strError)
226 {
227  std::string strAction = "New vote inserted:";
228  const COutPoint& mnId = vote.GetVin().prevout;
229  const int64_t voteTime = vote.GetTime();
230 
231  if (mapVotes.count(mnId)) {
232  const int64_t& oldTime = mapVotes[mnId].GetTime();
233  if (oldTime > voteTime) {
234  strError = strprintf("new vote older than existing vote - %s\n", vote.GetHash().ToString());
235  LogPrint(BCLog::MNBUDGET, "%s: %s\n", __func__, strError);
236  return false;
237  }
238  if (voteTime - oldTime < BUDGET_VOTE_UPDATE_MIN) {
239  strError = strprintf("time between votes is too soon - %s - %lli sec < %lli sec\n",
240  vote.GetHash().ToString(), voteTime - oldTime, BUDGET_VOTE_UPDATE_MIN);
241  LogPrint(BCLog::MNBUDGET, "%s: %s\n", __func__, strError);
242  return false;
243  }
244  strAction = "Existing vote updated:";
245  }
246 
247  mapVotes[mnId] = vote;
248  LogPrint(BCLog::MNBUDGET, "%s: %s %s\n", __func__, strAction.c_str(), vote.GetHash().ToString().c_str());
249 
250  return true;
251 }
252 
254 {
256  for (const auto& it: mapVotes) {
257  ret.push_back(it.second.ToJSON());
258  }
259  return ret;
260 }
261 
262 void CBudgetProposal::SetSynced(bool synced)
263 {
264  for (auto& it: mapVotes) {
265  CBudgetVote& vote = it.second;
266  if (synced) {
267  if (vote.IsValid()) vote.SetSynced(true);
268  } else {
269  vote.SetSynced(false);
270  }
271  }
272 }
273 
275 {
276  int yeas = GetYeas();
277  int nays = GetNays();
278 
279  if (yeas + nays == 0) return 0.0f;
280 
281  return ((double)(yeas) / (double)(yeas + nays));
282 }
283 
285 {
286  int ret = 0;
287  for (const auto& it : mapVotes) {
288  const CBudgetVote& vote = it.second;
289  if (vote.GetDirection() == vd && vote.IsValid())
290  ret++;
291  }
292  return ret;
293 }
294 
296 {
297  //end block is half way through the next cycle (so the proposal will be removed much after the payment is sent)
298  return GetBlockCycle(nBlockStart);
299 }
300 
302 {
303  return nHeight - nHeight % Params().GetConsensus().nBudgetCycleBlocks;
304 }
305 
307 {
308  return nBlockEnd;
309 
310 }
311 
313 {
315 }
316 
317 int CBudgetProposal::GetRemainingPaymentCount(int nCurrentHeight) const
318 {
319  // If the proposal is already finished (passed the end block cycle), the payments value will be negative
320  int nPayments = (GetBlockEndCycle() - GetBlockCycle(nCurrentHeight)) / Params().GetConsensus().nBudgetCycleBlocks - 1;
321  // Take the lowest value
322  return std::min(nPayments, GetTotalPaymentCount());
323 }
324 
325 // return broadcast serialization
327 {
328  CDataStream broadcast(SER_NETWORK, PROTOCOL_VERSION);
329  broadcast.reserve(1000);
330  broadcast << LIMITED_STRING(strProposalName, 20);
331  broadcast << LIMITED_STRING(strURL, 64);
332  broadcast << nTime;
333  broadcast << nBlockStart;
334  broadcast << nBlockEnd;
335  broadcast << nAmount;
336  broadcast << address;
337  broadcast << nFeeTXHash;
338  return broadcast;
339 }
340 
342 {
344  g_connman->RelayInv(inv);
345 }
346 
int64_t CAmount
Amount in PIV (Can be negative)
Definition: amount.h:13
true
Definition: bls_dkg.cpp:153
const CChainParams & Params()
Return the currently selected parameters.
void reserve(size_type n)
Definition: streams.h:168
int GetYeas() const
double GetRatio() const
std::map< COutPoint, CBudgetVote > mapVotes
int GetRemainingPaymentCount(int nCurrentHeight) const
bool IsExpired(int nCurrentHeight) const
CDataStream GetBroadcast() const
bool updateExpired(int nCurrentHeight)
bool CheckAmount(const CAmount &nTotalBudget)
static int GetBlockCycle(int nCurrentHeight)
int GetBlockEndCycle() const
int GetVoteCount(CBudgetVote::VoteDirection vd) const
std::string strInvalid
bool ParseBroadcast(CDataStream &broadcast)
void SetSynced(bool synced)
bool AddOrUpdateVote(const CBudgetVote &vote, std::string &strError)
std::string strProposalName
bool UpdateValid(int nHeight, int mnCount)
bool IsHeavilyDownvoted(int mnCount)
std::string strURL
bool IsWellFormed(const CAmount &nTotalBudget)
int GetNays() const
void SyncVotes(CNode *pfrom, bool fPartial, int &nInvCount) const
uint256 GetHash() const
int GetTotalPaymentCount() const
bool IsEstablished() const
UniValue GetVotesArray() const
bool IsPassing(int nBlockStartBudget, int nBlockEndBudget, int mnCount) const
int GetBlockStartCycle() const
CTxIn GetVin() const
Definition: budgetvote.h:54
bool IsValid() const
Definition: budgetvote.h:62
bool IsSynced() const
Definition: budgetvote.h:61
int64_t GetTime() const
Definition: budgetvote.h:60
uint256 GetHash() const
Definition: budgetvote.cpp:38
void SetSynced(bool _fSynced)
Definition: budgetvote.h:64
VoteDirection GetDirection() const
Definition: budgetvote.h:58
const Consensus::Params & GetConsensus() const
Definition: chainparams.h:72
inv message data
Definition: protocol.h:466
Information about a peer.
Definition: net.h:669
void PushInventory(const CInv &inv)
Definition: net.h:914
An outpoint - a combination of a transaction hash and an index n into its vout.
Definition: transaction.h:72
Serialized script, used inside transaction inputs and outputs.
Definition: script.h:381
bool IsPayToScriptHash() const
Definition: script.cpp:223
COutPoint prevout
Definition: transaction.h:96
@ VARR
Definition: univalue.h:21
bool push_back(const UniValue &val)
Definition: univalue.cpp:108
std::string ToString() const
Definition: uint256.cpp:65
256-bit opaque blob.
Definition: uint256.h:138
std::unique_ptr< CConnman > g_connman
Definition: init.cpp:90
#define LogPrint(category,...)
Definition: logging.h:163
@ MNBUDGET
Definition: logging.h:60
bool IsValidDestination(const CWDestination &address)
@ MSG_BUDGET_VOTE
Definition: protocol.h:446
@ MSG_BUDGET_PROPOSAL
Definition: protocol.h:447
const char * name
Definition: rest.cpp:37
const char * url
Definition: rpcconsole.cpp:51
@ SER_NETWORK
Definition: serialize.h:174
#define LIMITED_STRING(obj, n)
Definition: serialize.h:515
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
int64_t nProposalEstablishmentTime
Definition: params.h:188
int nBudgetCycleBlocks
Definition: params.h:178
bool error(const char *fmt, const Args &... args)
Definition: system.h:77
int64_t GetAdjustedTime()
Definition: timedata.cpp:36
#define strprintf
Definition: tinyformat.h:1056
const uint256 UINT256_ZERO
constant uint256 instances
Definition: uint256.h:175
std::string SanitizeString(const std::string &str, int rule)
Remove unsafe chars.