diff --git a/Builds/VisualStudio2015/RippleD.vcxproj b/Builds/VisualStudio2015/RippleD.vcxproj index 8f65c0ae3a..79fcd1cd70 100644 --- a/Builds/VisualStudio2015/RippleD.vcxproj +++ b/Builds/VisualStudio2015/RippleD.vcxproj @@ -1914,6 +1914,10 @@ True True + + True + True + True True @@ -1956,6 +1960,8 @@ + + @@ -1994,6 +2000,10 @@ True True + + True + True + True True diff --git a/Builds/VisualStudio2015/RippleD.vcxproj.filters b/Builds/VisualStudio2015/RippleD.vcxproj.filters index fcd61ab9ff..8e33c75f13 100644 --- a/Builds/VisualStudio2015/RippleD.vcxproj.filters +++ b/Builds/VisualStudio2015/RippleD.vcxproj.filters @@ -2652,6 +2652,9 @@ ripple\basics\impl + + ripple\basics\impl + ripple\basics\impl @@ -2688,6 +2691,9 @@ ripple\basics + + ripple\basics + ripple\basics @@ -2733,6 +2739,9 @@ ripple\basics\tests + + ripple\basics\tests + ripple\basics\tests diff --git a/src/ripple/app/misc/impl/TxQ.cpp b/src/ripple/app/misc/impl/TxQ.cpp index 44c82a7f42..49dea107fe 100644 --- a/src/ripple/app/misc/impl/TxQ.cpp +++ b/src/ripple/app/misc/impl/TxQ.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -62,9 +63,10 @@ getFeeLevelPaid( // TODO: getRequiredFeeLevel(ttREFERENCE)? auto referenceFee = ripple::getRequiredFeeLevel(ttACCOUNT_SET); - return mulDivNoThrow(tx[sfFee].xrp().drops(), + // Don't care about the overflow flag + return mulDiv(tx[sfFee].xrp().drops(), baseRefLevel * referenceFee, - refTxnCostDrops * requiredFee); + refTxnCostDrops * requiredFee).second; } ////////////////////////////////////////////////////////////////////////// @@ -169,8 +171,9 @@ FeeMetrics::scaleFeeLevel(OpenView const& view) const if (current > target) { // Compute escalated fee level - fee = mulDivNoThrow(fee, current * current * - multiplier, target * target); + // Don't care about the overflow flag + fee = mulDiv(fee, current * current * + multiplier, target * target).second; } return fee; @@ -368,9 +371,10 @@ TxQ::apply(Application& app, OpenView& view, { // Is the current transaction's fee higher than // the queued transaction's fee? - auto requiredRetryLevel = mulDivNoThrow( + // Don't care about the overflow flag + auto requiredRetryLevel = mulDiv( existingCandidate->feeLevel, - setup_.retrySequencePercent, 100); + setup_.retrySequencePercent, 100).second; JLOG(j_.trace) << "Found transaction in queue for account " << account << " with sequence number " << sequence << " new txn fee level is " << feeLevelPaid << @@ -693,18 +697,19 @@ TxQ::doRPC(Application& app) const auto const baseFee = view->fees().base; auto& drops = ret[jss::drops] = Json::Value(); - drops[jss::base_fee] = to_string(mulDivNoThrow( + // Don't care about the overflow flags + drops[jss::base_fee] = to_string(mulDiv( metrics.referenceFeeLevel, baseFee, - metrics.referenceFeeLevel)); - drops[jss::minimum_fee] = to_string(mulDivNoThrow( + metrics.referenceFeeLevel).second); + drops[jss::minimum_fee] = to_string(mulDiv( metrics.minFeeLevel, baseFee, - metrics.referenceFeeLevel)); - drops[jss::median_fee] = to_string(mulDivNoThrow( + metrics.referenceFeeLevel).second); + drops[jss::median_fee] = to_string(mulDiv( metrics.medFeeLevel, baseFee, - metrics.referenceFeeLevel)); - drops[jss::open_ledger_fee] = to_string(mulDivNoThrow( + metrics.referenceFeeLevel).second); + drops[jss::open_ledger_fee] = to_string(mulDiv( metrics.expFeeLevel, baseFee, - metrics.referenceFeeLevel)); + metrics.referenceFeeLevel).second); return ret; } @@ -714,8 +719,9 @@ TxQ::openLedgerFee(OpenView const& view) const { auto metrics = getMetrics(view); - return mulDivNoThrow(metrics.expFeeLevel, - view.fees().base, metrics.referenceFeeLevel) + 1; + // Don't care about the overflow flag + return mulDiv(metrics.expFeeLevel, + view.fees().base, metrics.referenceFeeLevel).second + 1; } ////////////////////////////////////////////////////////////////////////// diff --git a/src/ripple/basics/impl/mulDiv.cpp b/src/ripple/basics/impl/mulDiv.cpp new file mode 100644 index 0000000000..83e768e754 --- /dev/null +++ b/src/ripple/basics/impl/mulDiv.cpp @@ -0,0 +1,65 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2012-2015 Ripple Labs Inc. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#include +#include +#include +#include +#include +#include + +namespace ripple +{ + +// compute (value)*(mul)/(div) - avoid overflow but keep precision +std::pair +mulDiv(std::uint64_t value, std::uint64_t mul, std::uint64_t div) +{ + if ((value == 0 || mul == 0) && div != 0) + return{ true, 0 }; + lowestTerms(value, div); + lowestTerms(mul, div); + + if (value < mul) + std::swap(value, mul); + constexpr std::uint64_t max = + std::numeric_limits::max(); + const auto limit = max / mul; + if (value > limit) + { + value /= div; + if (value > limit) + return{ false, max }; + return{ true, value * mul }; + } + return{ true, value * mul / div }; +} + +// compute (value)*(mul)/(div) - avoid overflow but keep precision +std::uint64_t +mulDivThrow(std::uint64_t value, std::uint64_t mul, std::uint64_t div) +{ + auto const result = mulDiv(value, mul, div); + if(!result.first) + Throw("mulDiv"); + return result.second; +} + + +} // ripple diff --git a/src/ripple/basics/mulDiv.h b/src/ripple/basics/mulDiv.h new file mode 100644 index 0000000000..cd7d41529f --- /dev/null +++ b/src/ripple/basics/mulDiv.h @@ -0,0 +1,80 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2012-2015 Ripple Labs Inc. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#ifndef RIPPLE_BASICS_MULDIV_H_INCLUDED +#define RIPPLE_BASICS_MULDIV_H_INCLUDED + +#include +#include +#include + +namespace ripple +{ + +/** Return value*mul/div accurately. + Computes the result of the multiplication and division in + a single step, avoiding overflow and retaining precision. + Throws: + None + Returns: + `std::pair`: + `first` is `false` if the calculation overflows, + `true` if the calculation is safe. + `second` is the result of the calculation if + `first` is `false`, max value of `uint64_t` + if `true`. +*/ +std::pair +mulDiv(std::uint64_t value, std::uint64_t mul, std::uint64_t div); + +/** Return value*mul/div accurately. + Computes the result of the multiplication and division in + a single step, avoiding overflow and retaining precision. + Throws: + std::overflow_error +*/ +std::uint64_t +mulDivThrow(std::uint64_t value, std::uint64_t mul, std::uint64_t div); + +template ::value && + std::is_unsigned::value && + sizeof(T1) <= sizeof(std::uint64_t) >, + class = std::enable_if_t < + std::is_integral::value && + std::is_unsigned::value && + sizeof(T2) <= sizeof(std::uint64_t) > +> +void lowestTerms(T1& a, T2& b) +{ + std::uint64_t x = a, y = b; + while (y != 0) + { + auto t = x % y; + x = y; + y = t; + } + a /= x; + b /= x; +} + +} // ripple + +#endif diff --git a/src/ripple/basics/tests/mulDiv.test.cpp b/src/ripple/basics/tests/mulDiv.test.cpp new file mode 100644 index 0000000000..2658556bd5 --- /dev/null +++ b/src/ripple/basics/tests/mulDiv.test.cpp @@ -0,0 +1,74 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2012-2016 Ripple Labs Inc. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#include +#include +#include + +namespace ripple { +namespace test { + +struct mulDiv_test : beast::unit_test::suite +{ + void run() + { + const auto max = std::numeric_limits::max(); + const std::uint64_t max32 = std::numeric_limits::max(); + + auto result = mulDiv(85, 20, 5); + expect(result.first && result.second == 340); + result = mulDiv(20, 85, 5); + expect(result.first && result.second == 340); + + result = mulDiv(0, max - 1, max - 3); + expect(result.first && result.second == 0); + result = mulDiv(max - 1, 0, max - 3); + expect(result.first && result.second == 0); + + result = mulDiv(max, 2, max / 2); + expect(result.first && result.second == 4); + result = mulDiv(max, 1000, max / 1000); + expect(result.first && result.second == 1000000); + result = mulDiv(max, 1000, max / 1001); + expect(result.first && result.second == 1001000); + // 2^64 / 5 = 3689348814741910323, but we lose some precision + // starting in the 10th digit to avoid the overflow. + result = mulDiv(max32 + 1, max32 + 1, 5); + expect(result.first && result.second == 3689348813882916864); + + // Overflow + result = mulDiv(max - 1, max - 2, 5); + expect(!result.first && result.second == max); + + try + { + mulDivThrow(max - 1, max - 2, 5); + fail(); + } + catch (std::overflow_error const& e) + { + expect(e.what() == std::string("mulDiv")); + } + } +}; + +BEAST_DEFINE_TESTSUITE(mulDiv, ripple_basics, ripple); + +} // namespace test +} // namespace ripple diff --git a/src/ripple/core/impl/LoadFeeTrack.cpp b/src/ripple/core/impl/LoadFeeTrack.cpp index c59fb32fc0..b742dd7075 100644 --- a/src/ripple/core/impl/LoadFeeTrack.cpp +++ b/src/ripple/core/impl/LoadFeeTrack.cpp @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -31,7 +32,7 @@ std::uint64_t LoadFeeTrack::scaleFeeBase (std::uint64_t fee, std::uint64_t baseFee, std::uint32_t referenceFeeUnits) const { - return mulDiv (fee, baseFee, referenceFeeUnits); + return mulDivThrow (fee, baseFee, referenceFeeUnits); } // Scale using load as well as base rate @@ -116,7 +117,7 @@ LoadFeeTrack::getJson (std::uint64_t baseFee, // load_fee = The cost to send a "reference" transaction now, // in millionths of a Ripple j[jss::load_fee] = Json::Value::UInt ( - mulDiv (baseFee, std::max (mLocalTxnLoadFee, + mulDivThrow(baseFee, std::max(mLocalTxnLoadFee, mRemoteTxnLoadFee), lftNormalFee)); } diff --git a/src/ripple/protocol/STAmount.h b/src/ripple/protocol/STAmount.h index 69fd45fca9..7328a7e26e 100644 --- a/src/ripple/protocol/STAmount.h +++ b/src/ripple/protocol/STAmount.h @@ -418,35 +418,6 @@ inline bool isXRP(STAmount const& amount) return isXRP (amount.issue().currency); } -/** - A utility function to compute (value)*(mul)/(div) while avoiding - overflow but keeping precision. -*/ -std::uint64_t -mulDiv(std::uint64_t value, std::uint64_t mul, std::uint64_t div); - -/** - A utility function to compute (value)*(mul)/(div) while avoiding - overflow but keeping precision. Will return the max uint64_t - value if mulDiv would overflow anyway. -*/ -std::uint64_t -mulDivNoThrow(std::uint64_t value, std::uint64_t mul, std::uint64_t div); - -template -void lowestTerms(T1& a, T2& b) -{ - std::uint64_t x = a, y = b; - while (y != 0) - { - auto t = x % y; - x = y; - y = t; - } - a /= x; - b /= x; -} - } // ripple #endif diff --git a/src/ripple/protocol/impl/STAmount.cpp b/src/ripple/protocol/impl/STAmount.cpp index 9b7ab22642..9ea925374c 100644 --- a/src/ripple/protocol/impl/STAmount.cpp +++ b/src/ripple/protocol/impl/STAmount.cpp @@ -1275,40 +1275,6 @@ divRound (STAmount const& num, STAmount const& den, return result; } -// compute (value)*(mul)/(div) - avoid overflow but keep precision -std::uint64_t -mulDiv(std::uint64_t value, std::uint64_t mul, std::uint64_t div) -{ - lowestTerms(value, div); - lowestTerms(mul, div); - - if (value < mul) - std::swap(value, mul); - const auto max = std::numeric_limits::max(); - if (value > max / mul) - { - value /= div; - if (value > max / mul) - Throw ("mulDiv"); - return value * mul; - } - return value * mul / div; -} - -// compute (value)*(mul)/(div) - avoid overflow but keep precision -std::uint64_t -mulDivNoThrow(std::uint64_t value, std::uint64_t mul, std::uint64_t div) -{ - try - { - return mulDiv(value, mul, div); - } - catch (std::overflow_error) - { - return std::numeric_limits::max(); - } -} - NetClock::time_point STAmountCalcSwitchovers::enableUnderflowFixCloseTime () { diff --git a/src/ripple/rpc/impl/TransactionSign.cpp b/src/ripple/rpc/impl/TransactionSign.cpp index 6e5ca9075c..18317392df 100644 --- a/src/ripple/rpc/impl/TransactionSign.cpp +++ b/src/ripple/rpc/impl/TransactionSign.cpp @@ -25,6 +25,7 @@ #include #include // Validity::Valid #include +#include #include #include #include diff --git a/src/ripple/unity/basics.cpp b/src/ripple/unity/basics.cpp index 93098ed23c..8ba351a5a5 100644 --- a/src/ripple/unity/basics.cpp +++ b/src/ripple/unity/basics.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -36,6 +37,7 @@ #include #include +#include #include #include #include