diff --git a/src/ripple/data/protocol/STAmount.cpp b/src/ripple/data/protocol/STAmount.cpp index 0bca31763..bb9e59170 100644 --- a/src/ripple/data/protocol/STAmount.cpp +++ b/src/ripple/data/protocol/STAmount.cpp @@ -371,21 +371,27 @@ STAmount operator- (STAmount const& v1, STAmount const& v2) //------------------------------------------------------------------------------ -std::uint64_t STAmount::uRateOne = - getRate (STAmount (1), STAmount (1)); +std::uint64_t const STAmount::uRateOne = getRate (STAmount (1), STAmount (1)); // Note: mIsNative and mIssue.currency must be set already! bool -STAmount::setValue (std::string const& sAmount) +STAmount::setValue (std::string const& amount) { - static boost::regex reNumber ( - "\\`([+-]?)(\\d*)(\\.(\\d*))?([eE]([+-]?)(\\d+))?\\'"); - boost::smatch smMatch; + static boost::regex const reNumber ( + "^" // the beginning of the string + "([-+]?)" // (optional) + or - character + "(0|[1-9][0-9]*)" // a number (no leading zeroes, unless 0) + "(\\.([0-9]+))?" // (optional) period followed by any number + "([eE]([+-]?)([0-9]+))?" // (optional) E, optional + or -, any number + "$", + boost::regex_constants::optimize); - if (!boost::regex_match (sAmount, smMatch, reNumber)) + boost::smatch match; + + if (!boost::regex_match (amount, match, reNumber)) { - WriteLog (lsWARNING, STAmount) - << "Number not valid: \"" << sAmount << "\""; + WriteLog (lsWARNING, STAmount) << + "Number not valid: \"" << amount << "\""; return false; } @@ -401,113 +407,56 @@ STAmount::setValue (std::string const& sAmount) try { - if ((smMatch[2].length () + smMatch[4].length ()) > 32) + // CHECKME: Why 32? Shouldn't this be 16? + if ((match[2].length () + match[4].length ()) > 32) { - WriteLog (lsWARNING, STAmount) << "Overlong number: " << sAmount; + WriteLog (lsWARNING, STAmount) << "Overlong number: " << amount; return false; } - mIsNegative = (smMatch[1].matched && (smMatch[1] == "-")); + mIsNegative = (match[1].matched && (match[1] == "-")); - if (!smMatch[4].matched) // integer only + // Can't specify XRP using fractional representation + if (mIsNative && match[3].matched) + return false; + + if (!match[4].matched) // integer only { - mValue = beast::lexicalCast (std::string (smMatch[2])); + mValue = beast::lexicalCastThrow (std::string (match[2])); mOffset = 0; } else { // integer and fraction - mValue = beast::lexicalCast (smMatch[2] + smMatch[4]); - mOffset = - (smMatch[4].length ()); + mValue = beast::lexicalCastThrow (match[2] + match[4]); + mOffset = -(match[4].length ()); } - if (smMatch[5].matched) + if (match[5].matched) { // we have an exponent - if (smMatch[6].matched && (smMatch[6] == "-")) - mOffset -= beast::lexicalCast (std::string (smMatch[7])); + if (match[6].matched && (match[6] == "-")) + mOffset -= beast::lexicalCastThrow (std::string (match[7])); else - mOffset += beast::lexicalCast (std::string (smMatch[7])); + mOffset += beast::lexicalCastThrow (std::string (match[7])); } + + canonicalize (); + + WriteLog (lsTRACE, STAmount) << + "Canonicalized \"" << amount << "\" to " << mValue << " : " << mOffset; } catch (...) { - WriteLog (lsWARNING, STAmount) << "Number not parsed: \"" << sAmount << "\""; return false; } - WriteLog (lsTRACE, STAmount) << "Float \"" << sAmount << "\" parsed to " << mValue << " : " << mOffset; - - if (mIsNative) - { - if (smMatch[3].matched) - mOffset -= SYSTEM_CURRENCY_PRECISION; - - while (mOffset > 0) - { - mValue *= 10; - --mOffset; - } - - while (mOffset < 0) - { - mValue /= 10; - ++mOffset; - } - } - else - canonicalize (); - return true; } -// Not meant to be the ultimate parser. For use by RPC which is supposed to be sane and trusted. -// Native has special handling: -// - Integer values are in base units. -// - Float values are in float units. -// - To avoid a mistake float value for native are specified with a "^" in place of a "." -// <-- bValid: true = valid -bool STAmount::setFullValue (std::string const& sAmount, std::string const& sCurrency, std::string const& sIssuer) +void +STAmount::setIssue (Issue const& issue) { - // - // Figure out the currency. - // - if (!to_currency (mIssue.currency, sCurrency)) - { - WriteLog (lsINFO, STAmount) << "Currency malformed: " << sCurrency; - - return false; - } - - mIsNative = !mIssue.currency; - - // - // Figure out the issuer. - // - RippleAddress naIssuerID; - - // Issuer must be "" or a valid account string. - if (!naIssuerID.setAccountID (sIssuer)) - { - WriteLog (lsINFO, STAmount) << "Issuer malformed: " << sIssuer; - - return false; - } - - mIssue.account = naIssuerID.getAccountID (); - - // Stamps not must have an issuer. - if (mIsNative && !isXRP (*this)) - { - WriteLog (lsINFO, STAmount) << "Issuer specified for XRP: " << sIssuer; - - return false; - } - - return setValue (sAmount); -} - -void STAmount::setIssue (Issue const& issue) { mIssue = std::move(issue); mIsNative = isXRP (*this); } @@ -836,7 +785,7 @@ void STAmount::canonicalize () --mOffset; } - if (mValue > cMaxNative) + if (mValue > cMaxNativeN) throw std::runtime_error ("Native currency amount out of range"); return; @@ -870,7 +819,6 @@ void STAmount::canonicalize () { mValue = 0; mOffset = 0; - mIsNegative = false; } if (mOffset > cMaxOffset) diff --git a/src/ripple/data/protocol/STAmount.h b/src/ripple/data/protocol/STAmount.h index e8b298719..e75827781 100644 --- a/src/ripple/data/protocol/STAmount.h +++ b/src/ripple/data/protocol/STAmount.h @@ -65,7 +65,7 @@ public: static const std::uint64_t cNotNative = 0x8000000000000000ull; static const std::uint64_t cPosNative = 0x4000000000000000ull; - static std::uint64_t uRateOne; + static std::uint64_t const uRateOne; //-------------------------------------------------------------------------- @@ -261,9 +261,6 @@ public: // Make this private bool setValue (std::string const& sAmount); - bool setFullValue (std::string const& sAmount, - std::string const& sCurrency = "", std::string const& sIssuer = ""); - //-------------------------------------------------------------------------- // // SerializedType diff --git a/src/ripple/data/protocol/STAmount.test.cpp b/src/ripple/data/protocol/STAmount.test.cpp index 0c6b86574..7e93a8516 100644 --- a/src/ripple/data/protocol/STAmount.test.cpp +++ b/src/ripple/data/protocol/STAmount.test.cpp @@ -111,27 +111,81 @@ public: //-------------------------------------------------------------------------- + void testSetValue ( + std::string const& value, Issue const& issue, bool success = true) + { + STAmount amount (issue); + + bool const result = amount.setValue (value); + + expect (result == success, std::string ("parse ") + value); + + if (success) + expect (amount.getText () == value, std::string ("format ") + value); + } + void testSetValue () { - testcase ("set value"); + { + testcase ("set value (native)"); - STAmount saTmp; + Issue const xrp (xrpIssue ()); - #if 0 - // Check native floats - saTmp.setFullValue ("1^0"); - BOOST_CHECK_MESSAGE (SYSTEM_CURRENCY_PARTS == saTmp.getNValue (), "float integer failed"); - saTmp.setFullValue ("0^1"); - BOOST_CHECK_MESSAGE (SYSTEM_CURRENCY_PARTS / 10 == saTmp.getNValue (), "float fraction failed"); - saTmp.setFullValue ("0^12"); - BOOST_CHECK_MESSAGE (12 * SYSTEM_CURRENCY_PARTS / 100 == saTmp.getNValue (), "float fraction failed"); - saTmp.setFullValue ("1^2"); - BOOST_CHECK_MESSAGE (SYSTEM_CURRENCY_PARTS + (2 * SYSTEM_CURRENCY_PARTS / 10) == saTmp.getNValue (), "float combined failed"); - #endif + // fractional XRP (i.e. drops) + testSetValue ("1", xrp); + testSetValue ("22", xrp); + testSetValue ("333", xrp); + testSetValue ("4444", xrp); + testSetValue ("55555", xrp); + testSetValue ("666666", xrp); - // Check native integer - saTmp.setFullValue ("1"); - expect (1 == saTmp.getNValue (), "should be equal"); + // 1 XRP up to 100 billion, in powers of 10 (in drops) + testSetValue ("1000000", xrp); + testSetValue ("10000000", xrp); + testSetValue ("100000000", xrp); + testSetValue ("1000000000", xrp); + testSetValue ("10000000000", xrp); + testSetValue ("100000000000", xrp); + testSetValue ("1000000000000", xrp); + testSetValue ("10000000000000", xrp); + testSetValue ("100000000000000", xrp); + testSetValue ("1000000000000000", xrp); + testSetValue ("10000000000000000", xrp); + testSetValue ("100000000000000000", xrp); + + // Invalid native values: + testSetValue ("1.1", xrp, false); + testSetValue ("100000000000000001", xrp, false); + testSetValue ("1000000000000000000", xrp, false); + } + + { + testcase ("set value (iou)"); + + Issue const usd (Currency (0x5553440000000000), Account (0x4985601)); + + testSetValue ("1", usd); + testSetValue ("10", usd); + testSetValue ("100", usd); + testSetValue ("1000", usd); + testSetValue ("10000", usd); + testSetValue ("100000", usd); + testSetValue ("1000000", usd); + testSetValue ("10000000", usd); + testSetValue ("100000000", usd); + testSetValue ("1000000000", usd); + testSetValue ("10000000000", usd); + + testSetValue ("1234567.1", usd); + testSetValue ("1234567.12", usd); + testSetValue ("1234567.123", usd); + testSetValue ("1234567.1234", usd); + testSetValue ("1234567.12345", usd); + testSetValue ("1234567.123456", usd); + testSetValue ("1234567.1234567", usd); + testSetValue ("1234567.12345678", usd); + testSetValue ("1234567.123456789", usd); + } } //--------------------------------------------------------------------------