diff options
| author | Bitflash <admin@bitflash.network> | 2026-08-11 11:04:03 -0300 |
|---|---|---|
| committer | Bitflash <admin@bitflash.network> | 2026-08-11 11:04:03 -0300 |
| commit | a83fae37099cb102beb5a4c174a4abe6ef3f24a4 (patch) | |
| tree | e2ec970053c813bcb330f0913953f030ecc6d19e | |
| parent | 3442c1c7f4b0476840b4ab3fab244eb000be0d04 (diff) | |
| parent | faf6c4d24304b770c2d43515da04832b2124c92a (diff) | |
Merge pull request 'money: ParseMoney accepts full 8 decimals' (#23) from parsemoney-full-decimals into main
| -rw-r--r-- | src/Makefile | 1 | ||||
| -rw-r--r-- | src/selftest.cpp | 37 | ||||
| -rw-r--r-- | src/util.cpp | 23 |
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 | ||
| 1221 | static 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 | |||
| 1221 | int RunSelfTest(const std::string& name) | 1256 | int 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) | |||
| 337 | bool ParseMoney(const char* pszIn, int64& nRet) | 337 | bool 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 | } |