Add units to all fee calculations:

* Uses existing XRPAmount with units for drops, and a new TaggedFee for
  fee units (LoadFeeTrack), and fee levels (TxQ).
* Resolves #2451
This commit is contained in:
Edward Hennis
2019-02-14 18:59:07 -05:00
parent 1901b981f3
commit e3b5b808c5
87 changed files with 2195 additions and 681 deletions

View File

@@ -19,6 +19,7 @@
#include <ripple/app/misc/LoadFeeTrack.h>
#include <ripple/basics/contract.h>
#include <ripple/basics/FeeUnits.h>
#include <ripple/basics/Log.h>
#include <ripple/basics/safe_cast.h>
#include <ripple/core/Config.h>
@@ -83,27 +84,32 @@ LoadFeeTrack::lowerLocalFee ()
//------------------------------------------------------------------------------
template <class T1, class T2,
class = std::enable_if_t<
std::is_integral_v<T1> &&
std::is_integral_v<T2>>
>
void lowestTerms(T1& a, T2& b)
{
if (auto const gcd = std::gcd(a, b))
{
a /= gcd;
b /= gcd;
}
}
// Scale using load as well as base rate
std::uint64_t
scaleFeeLoad(std::uint64_t fee, LoadFeeTrack const& feeTrack,
XRPAmount
scaleFeeLoad(FeeUnit64 fee, LoadFeeTrack const& feeTrack,
Fees const& fees, bool bUnlimited)
{
if (fee == 0)
return fee;
return XRPAmount{0};
// Normally, types with different units wouldn't be mathematically
// compatible. This function is an exception.
auto lowestTerms = [](auto& a, auto& b)
{
auto value = [](auto val)
{
if constexpr(std::is_arithmetic_v<decltype(val)>)
return val;
else
return val.value();
};
if (auto const g = std::gcd(value(a), value(b)))
{
a = value(a) / g;
b = value(b) / g;
}
};
// Collect the fee rates
auto [feeFactor, uRemFee] = feeTrack.getScalingFactors();
@@ -113,7 +119,7 @@ scaleFeeLoad(std::uint64_t fee, LoadFeeTrack const& feeTrack,
if (bUnlimited && (feeFactor > uRemFee) && (feeFactor < (4 * uRemFee)))
feeFactor = uRemFee;
auto baseFee = fees.base;
XRPAmount baseFee{fees.base};
// Compute:
// fee = fee * baseFee * feeFactor / (fees.units * lftNormalFee);
// without overflow, and as accurately as possible
@@ -121,7 +127,7 @@ scaleFeeLoad(std::uint64_t fee, LoadFeeTrack const& feeTrack,
// The denominator of the fraction we're trying to compute.
// fees.units and lftNormalFee are both 32 bit,
// so the multiplication can't overflow.
auto den = safe_cast<std::uint64_t>(fees.units)
auto den = FeeUnit64{ fees.units }
* safe_cast<std::uint64_t>(feeTrack.getLoadBase());
// Reduce fee * baseFee * feeFactor / (fees.units * lftNormalFee)
// to lowest terms.
@@ -131,33 +137,30 @@ scaleFeeLoad(std::uint64_t fee, LoadFeeTrack const& feeTrack,
// fee and baseFee are 64 bit, feeFactor is 32 bit
// Order fee and baseFee largest first
if (fee < baseFee)
std::swap(fee, baseFee);
// Normally, these types wouldn't be comparable or swappable.
// This function is an exception.
if (fee.value() < baseFee.value())
{
auto tmp = fee.value();
fee = baseFee.value();
baseFee = tmp;
}
// double check
assert(fee.value() >= baseFee.value());
// If baseFee * feeFactor overflows, the final result will overflow
const auto max = std::numeric_limits<std::uint64_t>::max();
if (baseFee > max / feeFactor)
Throw<std::overflow_error> ("scaleFeeLoad");
XRPAmount const baseFeeOverflow{
std::numeric_limits<XRPAmount::value_type>::max() / feeFactor};
if (baseFee > baseFeeOverflow)
{
Throw<std::overflow_error>("scaleFeeLoad");
}
baseFee *= feeFactor;
// Reorder fee and baseFee
if (fee < baseFee)
std::swap(fee, baseFee);
// If fee * baseFee / den might overflow...
if (fee > max / baseFee)
{
// Do the division first, on the larger of fee and baseFee
fee /= den;
if (fee > max / baseFee)
Throw<std::overflow_error> ("scaleFeeLoad");
fee *= baseFee;
}
else
{
// Otherwise fee * baseFee won't overflow,
// so do it prior to the division.
fee *= baseFee;
fee /= den;
}
return fee;
auto const result = mulDiv(fee, baseFee, den);
if (!result.first)
Throw<std::overflow_error> ("scaleFeeLoad");
return result.second;
}
} // ripple