Dash Core Source Documentation (0.16.0.1)

Find detailed information regarding the Dash Core source code.

governance-validators.cpp
Go to the documentation of this file.
1 // Copyright (c) 2014-2019 The Dash Core developers
2 // Distributed under the MIT/X11 software license, see the accompanying
3 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4 
6 
7 #include <base58.h>
8 #include <timedata.h>
9 #include <tinyformat.h>
10 #include <utilstrencodings.h>
11 
12 #include <algorithm>
13 
14 const size_t MAX_DATA_SIZE = 512;
15 const size_t MAX_NAME_SIZE = 40;
16 
17 CProposalValidator::CProposalValidator(const std::string& strHexData, bool fAllowLegacyFormat) :
18  objJSON(UniValue::VOBJ),
19  fJSONValid(false),
20  fAllowLegacyFormat(fAllowLegacyFormat),
21  strErrorMessages()
22 {
23  if (!strHexData.empty()) {
24  ParseStrHexData(strHexData);
25  }
26 }
27 
28 void CProposalValidator::ParseStrHexData(const std::string& strHexData)
29 {
30  std::vector<unsigned char> v = ParseHex(strHexData);
31  if (v.size() > MAX_DATA_SIZE) {
32  strErrorMessages = strprintf("data exceeds %lu characters;", MAX_DATA_SIZE);
33  return;
34  }
35  ParseJSONData(std::string(v.begin(), v.end()));
36 }
37 
38 bool CProposalValidator::Validate(bool fCheckExpiration)
39 {
40  if (!fJSONValid) {
41  strErrorMessages += "JSON parsing error;";
42  return false;
43  }
44  if (!ValidateName()) {
45  strErrorMessages += "Invalid name;";
46  return false;
47  }
48  if (!ValidateStartEndEpoch(fCheckExpiration)) {
49  strErrorMessages += "Invalid start:end range;";
50  return false;
51  }
52  if (!ValidatePaymentAmount()) {
53  strErrorMessages += "Invalid payment amount;";
54  return false;
55  }
56  if (!ValidatePaymentAddress()) {
57  strErrorMessages += "Invalid payment address;";
58  return false;
59  }
60  if (!ValidateURL()) {
61  strErrorMessages += "Invalid URL;";
62  return false;
63  }
64  return true;
65 }
66 
68 {
69  std::string strName;
70  if (!GetDataValue("name", strName)) {
71  strErrorMessages += "name field not found;";
72  return false;
73  }
74 
75  if (strName.size() > MAX_NAME_SIZE) {
76  strErrorMessages += strprintf("name exceeds %lu characters;", MAX_NAME_SIZE);
77  return false;
78  }
79 
80  static const std::string strAllowedChars = "-_abcdefghijklmnopqrstuvwxyz0123456789";
81 
82  std::transform(strName.begin(), strName.end(), strName.begin(), ::tolower);
83 
84  if (strName.find_first_not_of(strAllowedChars) != std::string::npos) {
85  strErrorMessages += "name contains invalid characters;";
86  return false;
87  }
88 
89  return true;
90 }
91 
92 bool CProposalValidator::ValidateStartEndEpoch(bool fCheckExpiration)
93 {
94  int64_t nStartEpoch = 0;
95  int64_t nEndEpoch = 0;
96 
97  if (!GetDataValue("start_epoch", nStartEpoch)) {
98  strErrorMessages += "start_epoch field not found;";
99  return false;
100  }
101 
102  if (!GetDataValue("end_epoch", nEndEpoch)) {
103  strErrorMessages += "end_epoch field not found;";
104  return false;
105  }
106 
107  if (nEndEpoch <= nStartEpoch) {
108  strErrorMessages += "end_epoch <= start_epoch;";
109  return false;
110  }
111 
112  if (fCheckExpiration && nEndEpoch <= GetAdjustedTime()) {
113  strErrorMessages += "expired;";
114  return false;
115  }
116 
117  return true;
118 }
119 
121 {
122  double dValue = 0.0;
123 
124  if (!GetDataValue("payment_amount", dValue)) {
125  strErrorMessages += "payment_amount field not found;";
126  return false;
127  }
128 
129  if (dValue <= 0.0) {
130  strErrorMessages += "payment_amount is negative;";
131  return false;
132  }
133 
134  // TODO: Should check for an amount which exceeds the budget but this is
135  // currently difficult because start and end epochs are defined in terms of
136  // clock time instead of block height.
137 
138  return true;
139 }
140 
142 {
143  std::string strPaymentAddress;
144 
145  if (!GetDataValue("payment_address", strPaymentAddress)) {
146  strErrorMessages += "payment_address field not found;";
147  return false;
148  }
149 
150  if (std::find_if(strPaymentAddress.begin(), strPaymentAddress.end(), ::isspace) != strPaymentAddress.end()) {
151  strErrorMessages += "payment_address can't have whitespaces;";
152  return false;
153  }
154 
155  CTxDestination dest = DecodeDestination(strPaymentAddress);
156  if (!IsValidDestination(dest)) {
157  strErrorMessages += "payment_address is invalid;";
158  return false;
159  }
160 
161  const CScriptID *scriptID = boost::get<CScriptID>(&dest);
162  if (scriptID) {
163  strErrorMessages += "script addresses are not supported;";
164  return false;
165  }
166 
167  return true;
168 }
169 
171 {
172  std::string strURL;
173  if (!GetDataValue("url", strURL)) {
174  strErrorMessages += "url field not found;";
175  return false;
176  }
177 
178  if (std::find_if(strURL.begin(), strURL.end(), ::isspace) != strURL.end()) {
179  strErrorMessages += "url can't have whitespaces;";
180  return false;
181  }
182 
183  if (strURL.size() < 4U) {
184  strErrorMessages += "url too short;";
185  return false;
186  }
187 
188  if (!CheckURL(strURL)) {
189  strErrorMessages += "url invalid;";
190  return false;
191  }
192 
193  return true;
194 }
195 
196 void CProposalValidator::ParseJSONData(const std::string& strJSONData)
197 {
198  fJSONValid = false;
199 
200  if (strJSONData.empty()) {
201  return;
202  }
203 
204  try {
206 
207  obj.read(strJSONData);
208 
209  if (obj.isObject()) {
210  objJSON = obj;
211  } else {
212  if (fAllowLegacyFormat) {
213  std::vector<UniValue> arr1 = obj.getValues();
214  std::vector<UniValue> arr2 = arr1.at(0).getValues();
215  objJSON = arr2.at(1);
216  } else {
217  throw std::runtime_error("Legacy proposal serialization format not allowed");
218  }
219  }
220 
221  fJSONValid = true;
222  } catch (std::exception& e) {
223  strErrorMessages += std::string(e.what()) + std::string(";");
224  } catch (...) {
225  strErrorMessages += "Unknown exception;";
226  }
227 }
228 
229 bool CProposalValidator::GetDataValue(const std::string& strKey, std::string& strValueRet)
230 {
231  bool fOK = false;
232  try {
233  strValueRet = objJSON[strKey].get_str();
234  fOK = true;
235  } catch (std::exception& e) {
236  strErrorMessages += std::string(e.what()) + std::string(";");
237  } catch (...) {
238  strErrorMessages += "Unknown exception;";
239  }
240  return fOK;
241 }
242 
243 bool CProposalValidator::GetDataValue(const std::string& strKey, int64_t& nValueRet)
244 {
245  bool fOK = false;
246  try {
247  const UniValue uValue = objJSON[strKey];
248  switch (uValue.getType()) {
249  case UniValue::VNUM:
250  nValueRet = uValue.get_int64();
251  fOK = true;
252  break;
253  default:
254  break;
255  }
256  } catch (std::exception& e) {
257  strErrorMessages += std::string(e.what()) + std::string(";");
258  } catch (...) {
259  strErrorMessages += "Unknown exception;";
260  }
261  return fOK;
262 }
263 
264 bool CProposalValidator::GetDataValue(const std::string& strKey, double& dValueRet)
265 {
266  bool fOK = false;
267  try {
268  const UniValue uValue = objJSON[strKey];
269  switch (uValue.getType()) {
270  case UniValue::VNUM:
271  dValueRet = uValue.get_real();
272  fOK = true;
273  break;
274  default:
275  break;
276  }
277  } catch (std::exception& e) {
278  strErrorMessages += std::string(e.what()) + std::string(";");
279  } catch (...) {
280  strErrorMessages += "Unknown exception;";
281  }
282  return fOK;
283 }
284 
285 /*
286  The purpose of this function is to replicate the behavior of the
287  Python urlparse function used by sentinel (urlparse.py). This function
288  should return false whenever urlparse raises an exception and true
289  otherwise.
290  */
291 bool CProposalValidator::CheckURL(const std::string& strURLIn)
292 {
293  std::string strRest(strURLIn);
294  std::string::size_type nPos = strRest.find(':');
295 
296  if (nPos != std::string::npos) {
297  //std::string strSchema = strRest.substr(0,nPos);
298 
299  if (nPos < strRest.size()) {
300  strRest = strRest.substr(nPos + 1);
301  } else {
302  strRest = "";
303  }
304  }
305 
306  // Process netloc
307  if ((strRest.size() > 2) && (strRest.substr(0, 2) == "//")) {
308  static const std::string strNetlocDelimiters = "/?#";
309 
310  strRest = strRest.substr(2);
311 
312  std::string::size_type nPos2 = strRest.find_first_of(strNetlocDelimiters);
313 
314  std::string strNetloc = strRest.substr(0, nPos2);
315 
316  if ((strNetloc.find('[') != std::string::npos) && (strNetloc.find(']') == std::string::npos)) {
317  return false;
318  }
319 
320  if ((strNetloc.find(']') != std::string::npos) && (strNetloc.find('[') == std::string::npos)) {
321  return false;
322  }
323  }
324 
325  return true;
326 }
void ParseStrHexData(const std::string &strHexData)
bool isObject() const
Definition: univalue.h:85
boost::variant< CNoDestination, CKeyID, CScriptID > CTxDestination
A txout script template with a specific destination.
Definition: standard.h:80
bool CheckURL(const std::string &strURLIn)
const std::vector< UniValue > & getValues() const
bool read(const char *raw, size_t len)
#define strprintf
Definition: tinyformat.h:1066
double get_real() const
bool IsValidDestination(const CTxDestination &dest)
Check whether a CTxDestination is a CNoDestination.
Definition: standard.cpp:281
CTxDestination DecodeDestination(const std::string &str)
Definition: base58.cpp:336
const std::string & get_str() const
enum VType getType() const
Definition: univalue.h:65
int64_t get_int64() const
void ParseJSONData(const std::string &strJSONData)
const size_t MAX_DATA_SIZE
false
Definition: bls_dkg.cpp:168
bool Validate(bool fCheckExpiration=true)
CProposalValidator(const std::string &strDataHexIn=std::string(), bool fAllowLegacyFormat=true)
const size_t MAX_NAME_SIZE
bool ValidateStartEndEpoch(bool fCheckExpiration=true)
int64_t GetAdjustedTime()
Definition: timedata.cpp:35
A reference to a CScript: the Hash160 of its serialization (see script.h)
Definition: standard.h:22
bool GetDataValue(const std::string &strKey, std::string &strValueRet)
std::vector< unsigned char > ParseHex(const char *psz)
Released under the MIT license