aboutsummaryrefslogtreecommitdiff
path: root/src/utilstrencodings.cpp
diff options
context:
space:
mode:
authorWladimir J. van der Laan <laanwj@gmail.com>2015-07-06 10:49:24 +0200
committerWladimir J. van der Laan <laanwj@gmail.com>2015-07-10 15:43:26 +0200
commit9cc91523dbec6441e327e1e4c83ba751a4680bec (patch)
treef7ad53eeb2e34d7773e067acf2a268076b1078ca /src/utilstrencodings.cpp
parentd0a10c1959176eb40c0ec47a56de00820c59066d (diff)
rpc: Accept scientific notation for monetary amounts in JSON
Add a function `ParseFixedPoint` that parses numbers according to the JSON number specification and returns a 64-bit integer. Then this in `AmountFromValue`, rather than `ParseMoney`. Also add lots of tests (thanks to @jonasschnelli for some of them). Fixes issue #6297.
Diffstat (limited to 'src/utilstrencodings.cpp')
-rw-r--r--src/utilstrencodings.cpp120
1 files changed, 120 insertions, 0 deletions
diff --git a/src/utilstrencodings.cpp b/src/utilstrencodings.cpp
index 7d1de7d6a8..7208ca9474 100644
--- a/src/utilstrencodings.cpp
+++ b/src/utilstrencodings.cpp
@@ -538,3 +538,123 @@ int atoi(const std::string& str)
{
return atoi(str.c_str());
}
+
+/** Upper bound for mantissa.
+ * 10^18-1 is the largest arbitrary decimal that will fit in a signed 64-bit integer.
+ * Larger integers cannot consist of arbitrary combinations of 0-9:
+ *
+ * 999999999999999999 1^18-1
+ * 9223372036854775807 (1<<63)-1 (max int64_t)
+ * 9999999999999999999 1^19-1 (would overflow)
+ */
+static const int64_t UPPER_BOUND = 1000000000000000000LL - 1LL;
+
+/** Helper function for ParseFixedPoint */
+static inline bool ProcessMantissaDigit(char ch, int64_t &mantissa, int &mantissa_tzeros)
+{
+ if(ch == '0')
+ ++mantissa_tzeros;
+ else {
+ for (int i=0; i<=mantissa_tzeros; ++i) {
+ if (mantissa > (UPPER_BOUND / 10LL))
+ return false; /* overflow */
+ mantissa *= 10;
+ }
+ mantissa += ch - '0';
+ mantissa_tzeros = 0;
+ }
+ return true;
+}
+
+bool ParseFixedPoint(const std::string &val, int decimals, int64_t *amount_out)
+{
+ int64_t mantissa = 0;
+ int64_t exponent = 0;
+ int mantissa_tzeros = 0;
+ bool mantissa_sign = false;
+ bool exponent_sign = false;
+ int ptr = 0;
+ int end = val.size();
+ int point_ofs = 0;
+
+ if (ptr < end && val[ptr] == '-') {
+ mantissa_sign = true;
+ ++ptr;
+ }
+ if (ptr < end)
+ {
+ if (val[ptr] == '0') {
+ /* pass single 0 */
+ ++ptr;
+ } else if (val[ptr] >= '1' && val[ptr] <= '9') {
+ while (ptr < end && val[ptr] >= '0' && val[ptr] <= '9') {
+ if (!ProcessMantissaDigit(val[ptr], mantissa, mantissa_tzeros))
+ return false; /* overflow */
+ ++ptr;
+ }
+ } else return false; /* missing expected digit */
+ } else return false; /* empty string or loose '-' */
+ if (ptr < end && val[ptr] == '.')
+ {
+ ++ptr;
+ if (ptr < end && val[ptr] >= '0' && val[ptr] <= '9')
+ {
+ while (ptr < end && val[ptr] >= '0' && val[ptr] <= '9') {
+ if (!ProcessMantissaDigit(val[ptr], mantissa, mantissa_tzeros))
+ return false; /* overflow */
+ ++ptr;
+ ++point_ofs;
+ }
+ } else return false; /* missing expected digit */
+ }
+ if (ptr < end && (val[ptr] == 'e' || val[ptr] == 'E'))
+ {
+ ++ptr;
+ if (ptr < end && val[ptr] == '+')
+ ++ptr;
+ else if (ptr < end && val[ptr] == '-') {
+ exponent_sign = true;
+ ++ptr;
+ }
+ if (ptr < end && val[ptr] >= '0' && val[ptr] <= '9') {
+ while (ptr < end && val[ptr] >= '0' && val[ptr] <= '9') {
+ if (exponent > (UPPER_BOUND / 10LL))
+ return false; /* overflow */
+ exponent = exponent * 10 + val[ptr] - '0';
+ ++ptr;
+ }
+ } else return false; /* missing expected digit */
+ }
+ if (ptr != end)
+ return false; /* trailing garbage */
+
+ /* finalize exponent */
+ if (exponent_sign)
+ exponent = -exponent;
+ exponent = exponent - point_ofs + mantissa_tzeros;
+
+ /* finalize mantissa */
+ if (mantissa_sign)
+ mantissa = -mantissa;
+
+ /* convert to one 64-bit fixed-point value */
+ exponent += decimals;
+ if (exponent < 0)
+ return false; /* cannot represent values smaller than 10^-decimals */
+ if (exponent >= 18)
+ return false; /* cannot represent values larger than or equal to 10^(18-decimals) */
+
+ for (int i=0; i < exponent; ++i) {
+ if (mantissa > (UPPER_BOUND / 10LL) || mantissa < -(UPPER_BOUND / 10LL))
+ return false; /* overflow */
+ mantissa *= 10;
+ }
+ if (mantissa > UPPER_BOUND || mantissa < -UPPER_BOUND)
+ return false; /* overflow */
+
+ if (amount_out)
+ *amount_out = mantissa;
+
+ return true;
+}
+