mirror of
https://github.com/XRPLF/rippled.git
synced 2026-07-30 10:30:22 +00:00
Add optional enforcement of valid integer range to Number
This commit is contained in:
@@ -155,6 +155,13 @@ Number::Guard::round() noexcept
|
||||
|
||||
constexpr Number one{1000000000000000, -15, Number::unchecked{}};
|
||||
|
||||
void
|
||||
Number::checkInteger(char const* what) const
|
||||
{
|
||||
if (enforceInteger_ == strong && !valid())
|
||||
throw std::overflow_error(what);
|
||||
}
|
||||
|
||||
void
|
||||
Number::normalize()
|
||||
{
|
||||
@@ -207,9 +214,27 @@ Number::normalize()
|
||||
mantissa_ = -mantissa_;
|
||||
}
|
||||
|
||||
bool
|
||||
Number::valid() const noexcept
|
||||
{
|
||||
if (enforceInteger_ != none)
|
||||
{
|
||||
static Number const max = maxIntValue;
|
||||
static Number const maxNeg = -maxIntValue;
|
||||
// Avoid making a copy
|
||||
if (mantissa_ < 0)
|
||||
return *this >= maxNeg;
|
||||
return *this <= max;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Number&
|
||||
Number::operator+=(Number const& y)
|
||||
{
|
||||
// The strictest setting prevails
|
||||
enforceInteger_ = std::max(enforceInteger_, y.enforceInteger_);
|
||||
|
||||
if (y == Number{})
|
||||
return *this;
|
||||
if (*this == Number{})
|
||||
@@ -322,6 +347,9 @@ Number::operator+=(Number const& y)
|
||||
}
|
||||
mantissa_ = xm * xn;
|
||||
exponent_ = xe;
|
||||
|
||||
checkInteger("Number::addition integer overflow");
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -356,6 +384,9 @@ divu10(uint128_t& u)
|
||||
Number&
|
||||
Number::operator*=(Number const& y)
|
||||
{
|
||||
// The strictest setting prevails
|
||||
enforceInteger_ = std::max(enforceInteger_, y.enforceInteger_);
|
||||
|
||||
if (*this == Number{})
|
||||
return *this;
|
||||
if (y == Number{})
|
||||
@@ -422,12 +453,18 @@ Number::operator*=(Number const& y)
|
||||
XRPL_ASSERT(
|
||||
isnormal() || *this == Number{},
|
||||
"ripple::Number::operator*=(Number) : result is normal");
|
||||
|
||||
checkInteger("Number::multiplication integer overflow");
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Number&
|
||||
Number::operator/=(Number const& y)
|
||||
{
|
||||
// The strictest setting prevails
|
||||
enforceInteger_ = std::max(enforceInteger_, y.enforceInteger_);
|
||||
|
||||
if (y == Number{})
|
||||
throw std::overflow_error("Number: divide by 0");
|
||||
if (*this == Number{})
|
||||
@@ -455,6 +492,9 @@ Number::operator/=(Number const& y)
|
||||
exponent_ = ne - de - 17;
|
||||
mantissa_ *= np * dp;
|
||||
normalize();
|
||||
|
||||
checkInteger("Number::division integer overflow");
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include <xrpl/beast/unit_test.h>
|
||||
#include <xrpl/protocol/IOUAmount.h>
|
||||
#include <xrpl/protocol/STAmount.h>
|
||||
#include <xrpl/protocol/SystemParameters.h>
|
||||
|
||||
#include <sstream>
|
||||
#include <tuple>
|
||||
@@ -725,6 +726,172 @@ public:
|
||||
BEAST_EXPECT(Number(-100, -30000).truncate() == Number(0, 0));
|
||||
}
|
||||
|
||||
void
|
||||
testInteger()
|
||||
{
|
||||
testcase("Integer enforcement");
|
||||
|
||||
using namespace std::string_literals;
|
||||
|
||||
{
|
||||
Number a{100};
|
||||
BEAST_EXPECT(a.integerEnforcement() == Number::none);
|
||||
BEAST_EXPECT(a.valid());
|
||||
a = Number{1, 30};
|
||||
BEAST_EXPECT(a.valid());
|
||||
a = -100;
|
||||
BEAST_EXPECT(a.valid());
|
||||
}
|
||||
{
|
||||
Number a{100, Number::weak};
|
||||
BEAST_EXPECT(a.integerEnforcement() == Number::weak);
|
||||
BEAST_EXPECT(a.valid());
|
||||
a = Number{1, 30, Number::none};
|
||||
BEAST_EXPECT(!a.valid());
|
||||
a = -100;
|
||||
BEAST_EXPECT(a.integerEnforcement() == Number::weak);
|
||||
BEAST_EXPECT(a.valid());
|
||||
a = Number{5, Number::strong};
|
||||
BEAST_EXPECT(a.integerEnforcement() == Number::strong);
|
||||
BEAST_EXPECT(a.valid());
|
||||
}
|
||||
{
|
||||
Number a{100, Number::strong};
|
||||
BEAST_EXPECT(a.integerEnforcement() == Number::strong);
|
||||
BEAST_EXPECT(a.valid());
|
||||
try
|
||||
{
|
||||
a = Number{1, 30};
|
||||
BEAST_EXPECT(false);
|
||||
}
|
||||
catch (std::overflow_error const& e)
|
||||
{
|
||||
BEAST_EXPECT(e.what() == "Number::operator= integer overflow"s);
|
||||
// The throw is done _after_ the number is updated.
|
||||
BEAST_EXPECT((a == Number{1, 30}));
|
||||
}
|
||||
BEAST_EXPECT(!a.valid());
|
||||
a = -100;
|
||||
BEAST_EXPECT(a.integerEnforcement() == Number::strong);
|
||||
BEAST_EXPECT(a.valid());
|
||||
}
|
||||
{
|
||||
Number a{INITIAL_XRP.drops(), Number::weak};
|
||||
BEAST_EXPECT(!a.valid());
|
||||
a = -a;
|
||||
BEAST_EXPECT(!a.valid());
|
||||
|
||||
try
|
||||
{
|
||||
a.setIntegerEnforcement(Number::strong);
|
||||
BEAST_EXPECT(false);
|
||||
}
|
||||
catch (std::overflow_error const& e)
|
||||
{
|
||||
BEAST_EXPECT(
|
||||
e.what() ==
|
||||
"Number::setIntegerEnforcement integer overflow"s);
|
||||
// The throw is internal to the operator before the result is
|
||||
// assigned to the Number
|
||||
BEAST_EXPECT(a == -INITIAL_XRP);
|
||||
BEAST_EXPECT(!a.valid());
|
||||
}
|
||||
try
|
||||
{
|
||||
++a;
|
||||
BEAST_EXPECT(false);
|
||||
}
|
||||
catch (std::overflow_error const& e)
|
||||
{
|
||||
BEAST_EXPECT(e.what() == "Number::addition integer overflow"s);
|
||||
// The throw is internal to the operator before the result is
|
||||
// assigned to the Number
|
||||
BEAST_EXPECT(a == -INITIAL_XRP);
|
||||
BEAST_EXPECT(!a.valid());
|
||||
}
|
||||
a = Number::maxIntValue;
|
||||
try
|
||||
{
|
||||
++a;
|
||||
BEAST_EXPECT(false);
|
||||
}
|
||||
catch (std::overflow_error const& e)
|
||||
{
|
||||
BEAST_EXPECT(e.what() == "Number::addition integer overflow"s);
|
||||
// This time, the throw is done _after_ the number is updated.
|
||||
BEAST_EXPECT(a == Number::maxIntValue + 1);
|
||||
BEAST_EXPECT(!a.valid());
|
||||
}
|
||||
a = -Number::maxIntValue;
|
||||
try
|
||||
{
|
||||
--a;
|
||||
BEAST_EXPECT(false);
|
||||
}
|
||||
catch (std::overflow_error const& e)
|
||||
{
|
||||
BEAST_EXPECT(e.what() == "Number::addition integer overflow"s);
|
||||
// This time, the throw is done _after_ the number is updated.
|
||||
BEAST_EXPECT(a == -Number::maxIntValue - 1);
|
||||
BEAST_EXPECT(!a.valid());
|
||||
}
|
||||
a = Number(1, 10);
|
||||
try
|
||||
{
|
||||
a *= Number(1, 10);
|
||||
BEAST_EXPECT(false);
|
||||
}
|
||||
catch (std::overflow_error const& e)
|
||||
{
|
||||
BEAST_EXPECT(
|
||||
e.what() == "Number::multiplication integer overflow"s);
|
||||
// The throw is done _after_ the number is updated.
|
||||
BEAST_EXPECT((a == Number{1, 20}));
|
||||
BEAST_EXPECT(!a.valid());
|
||||
}
|
||||
try
|
||||
{
|
||||
a = Number::maxIntValue * 2;
|
||||
BEAST_EXPECT(false);
|
||||
}
|
||||
catch (std::overflow_error const& e)
|
||||
{
|
||||
BEAST_EXPECT(e.what() == "Number::operator= integer overflow"s);
|
||||
// The throw is done _after_ the number is updated.
|
||||
BEAST_EXPECT((a == Number{2, 14}));
|
||||
BEAST_EXPECT(!a.valid());
|
||||
}
|
||||
try
|
||||
{
|
||||
a = Number(3, 15, Number::strong);
|
||||
BEAST_EXPECT(false);
|
||||
}
|
||||
catch (std::overflow_error const& e)
|
||||
{
|
||||
BEAST_EXPECT(e.what() == "Number::Number integer overflow"s);
|
||||
// The Number doesn't get updated because the ctor throws
|
||||
BEAST_EXPECT((a == Number{2, 14}));
|
||||
BEAST_EXPECT(!a.valid());
|
||||
}
|
||||
a = Number(1, 10);
|
||||
try
|
||||
{
|
||||
a /= Number(1, -10);
|
||||
BEAST_EXPECT(false);
|
||||
}
|
||||
catch (std::overflow_error const& e)
|
||||
{
|
||||
BEAST_EXPECT(e.what() == "Number::division integer overflow"s);
|
||||
// The throw is done _after_ the number is updated.
|
||||
BEAST_EXPECT((a == Number{1, 20}));
|
||||
BEAST_EXPECT(!a.valid());
|
||||
}
|
||||
a /= Number(1, 15);
|
||||
BEAST_EXPECT((a == Number{1, 5}));
|
||||
BEAST_EXPECT(a.valid());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
run() override
|
||||
{
|
||||
@@ -746,6 +913,7 @@ public:
|
||||
test_inc_dec();
|
||||
test_toSTAmount();
|
||||
test_truncate();
|
||||
testInteger();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user