upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile1
-rw-r--r--src/selftest.cpp37
-rw-r--r--src/util.cpp23
3 files changed, 51 insertions, 10 deletions
diff --git a/src/Makefile b/src/Makefile
index bc15ef8..00a8182 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -192,5 +192,6 @@ tests: test_bip32 bitflash-node
192 ./bitflash-node -selftest=net-message 192 ./bitflash-node -selftest=net-message
193 ./bitflash-node -selftest=consensus-limits 193 ./bitflash-node -selftest=consensus-limits
194 ./bitflash-node -selftest=pool-stratum 194 ./bitflash-node -selftest=pool-stratum
195 ./bitflash-node -selftest=parse-money
195 196
196.PHONY: tests fuzz-net-message-smoke fuzz-script-smoke 197.PHONY: tests fuzz-net-message-smoke fuzz-script-smoke
diff --git a/src/selftest.cpp b/src/selftest.cpp
index 69a3596..dcb9192 100644
--- a/src/selftest.cpp
+++ b/src/selftest.cpp
@@ -1218,6 +1218,41 @@ static int RunConsensusLimitsSelfTest()
1218 return nFail == 0 ? 0 : 1; 1218 return nFail == 0 ? 0 : 1;
1219} 1219}
1220 1220
1221static int RunParseMoneySelfTest()
1222{
1223 printf("parse-money self-test\n");
1224 int nFail = 0;
1225 struct Case { const char* in; bool ok; int64 val; };
1226 Case cases[] = {
1227 {"0.5", true, 50000000LL},
1228 {"1", true, 100000000LL},
1229 {"1.23456789", true, 123456789LL},
1230 {"0.00000001", true, 1LL},
1231 {"10.00000000", true, 1000000000LL},
1232 {"1,000", true, 100000000000LL},
1233 {"1.5 ", true, 150000000LL},
1234 {"0.123456789", false, 0LL},
1235 {"1.2.3", false, 0LL},
1236 {"abc", false, 0LL},
1237 {"-1", false, 0LL},
1238 {"1e5", false, 0LL},
1239 };
1240 for (size_t i = 0; i < sizeof(cases) / sizeof(cases[0]); i++)
1241 {
1242 int64 v = -1;
1243 bool r = ParseMoney(cases[i].in, v);
1244 bool pass = (r == cases[i].ok) && (!r || v == cases[i].val);
1245 char desc[160];
1246 snprintf(desc, sizeof(desc), "ParseMoney(\"%s\") %s and value matches",
1247 cases[i].in, cases[i].ok ? "accepts" : "rejects");
1248 nFail += Check(pass, desc) ? 0 : 1;
1249 }
1250 printf("%s (%d failure%s)\n", nFail == 0 ? "ALL TESTS PASSED" : "TESTS FAILED",
1251 nFail, nFail == 1 ? "" : "s");
1252 fflush(stdout);
1253 return nFail == 0 ? 0 : 1;
1254}
1255
1221int RunSelfTest(const std::string& name) 1256int RunSelfTest(const std::string& name)
1222{ 1257{
1223 AttachTerminal(); 1258 AttachTerminal();
@@ -1238,6 +1273,8 @@ int RunSelfTest(const std::string& name)
1238 return RunConsensusLimitsSelfTest(); 1273 return RunConsensusLimitsSelfTest();
1239 if (name == "pool-stratum") 1274 if (name == "pool-stratum")
1240 return RunPoolStratumSelfTest(); 1275 return RunPoolStratumSelfTest();
1276 if (name == "parse-money")
1277 return RunParseMoneySelfTest();
1241 1278
1242 printf("Unknown self-test '%s'\n", name.c_str()); 1279 printf("Unknown self-test '%s'\n", name.c_str());
1243 printf("Known self-tests: wallet-keypool, wallet-hd, wallet-format, wallet-crypto, wallet-encrypt, net-message, consensus-limits, pool-stratum\n"); 1280 printf("Known self-tests: wallet-keypool, wallet-hd, wallet-format, wallet-crypto, wallet-encrypt, net-message, consensus-limits, pool-stratum\n");
diff --git a/src/util.cpp b/src/util.cpp
index d52ca88..0cd0133 100644
--- a/src/util.cpp
+++ b/src/util.cpp
@@ -337,7 +337,7 @@ string FormatMoney(int64 n, bool fPlus)
337bool ParseMoney(const char* pszIn, int64& nRet) 337bool ParseMoney(const char* pszIn, int64& nRet)
338{ 338{
339 string strWhole; 339 string strWhole;
340 int64 nCents = 0; 340 int64 nUnits = 0; // fractional part, scaled to COIN (8 decimals)
341 const char* p = pszIn; 341 const char* p = pszIn;
342 while (isspace(*p)) 342 while (isspace(*p))
343 p++; 343 p++;
@@ -348,12 +348,16 @@ bool ParseMoney(const char* pszIn, int64& nRet)
348 if (*p == '.') 348 if (*p == '.')
349 { 349 {
350 p++; 350 p++;
351 if (!isdigit(p[0]) || !isdigit(p[1])) 351 int64 nMult = COIN / 10; // weight of the first decimal digit
352 while (isdigit(*p) && nMult > 0)
353 {
354 nUnits += nMult * (*p - '0');
355 nMult /= 10;
356 p++;
357 }
358 // reject more than 8 fractional digits rather than silently truncate
359 if (isdigit(*p))
352 return false; 360 return false;
353 nCents = atoi64(p);
354 if (nCents < 0 || nCents > 99)
355 return false;
356 p += 2;
357 break; 361 break;
358 } 362 }
359 if (isspace(*p)) 363 if (isspace(*p))
@@ -365,13 +369,12 @@ bool ParseMoney(const char* pszIn, int64& nRet)
365 for (; *p; p++) 369 for (; *p; p++)
366 if (!isspace(*p)) 370 if (!isspace(*p))
367 return false; 371 return false;
368 if (strWhole.size() > 17) 372 if (strWhole.size() > 10) // 10 whole digits * COIN stays within int64
369 return false; 373 return false;
370 int64 nWhole = atoi64(strWhole); 374 int64 nWhole = atoi64(strWhole);
371 int64 nValue = nWhole * 100 + nCents; 375 int64 nValue = nWhole * COIN + nUnits;
372 if (nValue / 100 != nWhole) 376 if (nValue < 0 || (nValue - nUnits) / COIN != nWhole)
373 return false; 377 return false;
374 nValue *= CENT;
375 nRet = nValue; 378 nRet = nValue;
376 return true; 379 return true;
377} 380}