Convert STAmount switchovers to tls (RIPD-1068)

This commit is contained in:
Miguel Portilla
2016-01-08 15:00:55 -05:00
committed by Edward Hennis
parent 4fb6bf3e67
commit 14dde47173
17 changed files with 93 additions and 142 deletions

View File

@@ -43,9 +43,6 @@ TER PathCursor::deliverNodeForward (
// Zeroed in reverse pass. // Zeroed in reverse pass.
node().directory.restart(multiQuality_); node().directory.restart(multiQuality_);
STAmountCalcSwitchovers amountCalcSwitchovers (
rippleCalc_.view.info ().parentCloseTime);
saInAct.clear (saInReq); saInAct.clear (saInReq);
saInFees.clear (saInReq); saInFees.clear (saInReq);
@@ -110,11 +107,11 @@ TER PathCursor::deliverNodeForward (
saOutPassFunded, saOutPassFunded,
node().saOfrRate, node().saOfrRate,
node().saTakerPays.issue (), node().saTakerPays.issue (),
true, amountCalcSwitchovers); true);
// Offer maximum in with fees. // Offer maximum in with fees.
auto saInTotal = mulRound (saInFunded, saInFeeRate, auto saInTotal = mulRound (saInFunded, saInFeeRate,
saInFunded.issue (), true, amountCalcSwitchovers); saInFunded.issue (), true);
auto saInRemaining = saInReq - saInAct - saInFees; auto saInRemaining = saInReq - saInAct - saInFees;
if (saInRemaining < zero) if (saInRemaining < zero)
@@ -126,13 +123,11 @@ TER PathCursor::deliverNodeForward (
// In without fees. // In without fees.
auto saInPassAct = std::min ( auto saInPassAct = std::min (
node().saTakerPays, divRound ( node().saTakerPays, divRound (
saInSum, saInFeeRate, saInSum.issue (), true, saInSum, saInFeeRate, saInSum.issue (), true));
amountCalcSwitchovers));
// Out limited by in remaining. // Out limited by in remaining.
auto outPass = divRound ( auto outPass = divRound (
saInPassAct, node().saOfrRate, node().saTakerGets.issue (), true, saInPassAct, node().saOfrRate, node().saTakerGets.issue (), true);
amountCalcSwitchovers);
STAmount saOutPassMax = std::min (saOutPassFunded, outPass); STAmount saOutPassMax = std::min (saOutPassFunded, outPass);
STAmount saInPassFeesMax = saInSum - saInPassAct; STAmount saInPassFeesMax = saInSum - saInPassAct;
@@ -248,12 +243,10 @@ TER PathCursor::deliverNodeForward (
assert (saOutPassAct < saOutPassMax); assert (saOutPassAct < saOutPassMax);
auto inPassAct = mulRound ( auto inPassAct = mulRound (
saOutPassAct, node().saOfrRate, saInReq.issue (), true, saOutPassAct, node().saOfrRate, saInReq.issue (), true);
amountCalcSwitchovers);
saInPassAct = std::min (node().saTakerPays, inPassAct); saInPassAct = std::min (node().saTakerPays, inPassAct);
auto inPassFees = mulRound ( auto inPassFees = mulRound (
saInPassAct, saInFeeRate, saInPassAct.issue (), true, saInPassAct, saInFeeRate, saInPassAct.issue (), true);
amountCalcSwitchovers);
saInPassFees = std::min (saInPassFeesMax, inPassFees); saInPassFees = std::min (saInPassFeesMax, inPassFees);
} }

View File

@@ -45,9 +45,6 @@ TER PathCursor::deliverNodeReverseImpl (
{ {
TER resultCode = tesSUCCESS; TER resultCode = tesSUCCESS;
STAmountCalcSwitchovers amountCalcSwitchovers (
rippleCalc_.view.info ().parentCloseTime);
// Accumulation of what the previous node must deliver. // Accumulation of what the previous node must deliver.
// Possible optimization: Note this gets zeroed on each increment, ideally // Possible optimization: Note this gets zeroed on each increment, ideally
// only on first increment, then it could be a limit on the forward pass. // only on first increment, then it could be a limit on the forward pass.
@@ -160,8 +157,7 @@ TER PathCursor::deliverNodeReverseImpl (
// //
// Round down: prefer liquidity rather than microscopic fees. // Round down: prefer liquidity rather than microscopic fees.
STAmount saOutPlusFees = mulRound ( STAmount saOutPlusFees = mulRound (
saOutPassAct, saOutFeeRate, saOutPassAct.issue (), false, saOutPassAct, saOutFeeRate, saOutPassAct.issue (), false);
amountCalcSwitchovers);
// Offer out with fees. // Offer out with fees.
@@ -184,7 +180,7 @@ TER PathCursor::deliverNodeReverseImpl (
// Round up: prefer liquidity rather than microscopic fees. But, // Round up: prefer liquidity rather than microscopic fees. But,
// limit by requested. // limit by requested.
auto fee = divRound (saOutPlusFees, saOutFeeRate, auto fee = divRound (saOutPlusFees, saOutFeeRate,
saOutPlusFees.issue (), true, amountCalcSwitchovers); saOutPlusFees.issue (), true);
saOutPassAct = std::min (saOutPassReq, fee); saOutPassAct = std::min (saOutPassReq, fee);
JLOG (j_.trace) JLOG (j_.trace)
@@ -196,9 +192,8 @@ TER PathCursor::deliverNodeReverseImpl (
// Compute portion of input needed to cover actual output. // Compute portion of input needed to cover actual output.
auto outputFee = mulRound ( auto outputFee = mulRound (
saOutPassAct, node().saOfrRate, node().saTakerPays.issue (), true, saOutPassAct, node().saOfrRate, node().saTakerPays.issue (), true);
amountCalcSwitchovers); if (*stAmountCalcSwitchover == false && ! outputFee)
if (!amountCalcSwitchovers.enableUnderflowFix () && !outputFee)
{ {
JLOG (j_.fatal) JLOG (j_.fatal)
<< "underflow computing outputFee " << "underflow computing outputFee "
@@ -269,13 +264,11 @@ TER PathCursor::deliverNodeReverseImpl (
if (saInPassAct < saInPassReq) if (saInPassAct < saInPassReq)
{ {
// Adjust output to conform to limited input. // Adjust output to conform to limited input.
auto outputRequirements = divRound ( auto outputRequirements = divRound (saInPassAct, node ().saOfrRate,
saInPassAct, node ().saOfrRate, node ().saTakerGets.issue (), true, node ().saTakerGets.issue (), true);
amountCalcSwitchovers);
saOutPassAct = std::min (saOutPassReq, outputRequirements); saOutPassAct = std::min (saOutPassReq, outputRequirements);
auto outputFees = mulRound ( auto outputFees = mulRound (saOutPassAct, saOutFeeRate,
saOutPassAct, saOutFeeRate, saOutPassAct.issue (), true, saOutPassAct.issue (), true);
amountCalcSwitchovers);
saOutPlusFees = std::min (node().saOfferFunds, outputFees); saOutPlusFees = std::min (node().saOfferFunds, outputFees);
JLOG (j_.trace) JLOG (j_.trace)

View File

@@ -150,9 +150,6 @@ TER PathCursor::forwardLiquidityForAccount () const
<< " previousNode.saFwdRedeem:" << previousNode().saFwdRedeem << " previousNode.saFwdRedeem:" << previousNode().saFwdRedeem
<< " previousNode.saFwdIssue:" << previousNode().saFwdIssue; << " previousNode.saFwdIssue:" << previousNode().saFwdIssue;
STAmountCalcSwitchovers amountCalcSwitchovers (
rippleCalc_.view.info ().parentCloseTime);
// Last node. Accept all funds. Calculate amount actually to credit. // Last node. Accept all funds. Calculate amount actually to credit.
auto& saCurReceive = pathState_.outPass(); auto& saCurReceive = pathState_.outPass();
@@ -162,7 +159,7 @@ TER PathCursor::forwardLiquidityForAccount () const
previousNode().saFwdIssue, previousNode().saFwdIssue,
amountFromRate (uQualityIn), amountFromRate (uQualityIn),
previousNode().saFwdIssue.issue (), previousNode().saFwdIssue.issue (),
true, amountCalcSwitchovers); // Amount to credit. true); // Amount to credit.
// Amount to credit. Credit for less than received as a surcharge. // Amount to credit. Credit for less than received as a surcharge.
pathState_.setOutPass (previousNode().saFwdRedeem + saIssueCrd); pathState_.setOutPass (previousNode().saFwdRedeem + saIssueCrd);

View File

@@ -135,21 +135,16 @@ void rippleLiquidity (
// If the next rate is at least as good as the current rate, process. // If the next rate is at least as good as the current rate, process.
if (!uRateMax || uRate <= uRateMax) if (!uRateMax || uRate <= uRateMax)
{ {
STAmountCalcSwitchovers amountCalcSwitchovers (
rippleCalc.view.info ().parentCloseTime);
auto currency = saCur.getCurrency (); auto currency = saCur.getCurrency ();
auto uCurIssuerID = saCur.getIssuer (); auto uCurIssuerID = saCur.getIssuer ();
// current actual = current request * (quality out / quality in). // current actual = current request * (quality out / quality in).
auto numerator = mulRound ( auto numerator = mulRound (
saCur, uQualityOut, {currency, uCurIssuerID}, true, saCur, uQualityOut, {currency, uCurIssuerID}, true);
amountCalcSwitchovers);
// True means "round up" to get best flow. // True means "round up" to get best flow.
STAmount saCurIn = divRound ( STAmount saCurIn = divRound (
numerator, uQualityIn, {currency, uCurIssuerID}, true, numerator, uQualityIn, {currency, uCurIssuerID}, true);
amountCalcSwitchovers);
JLOG (rippleCalc.j_.trace) JLOG (rippleCalc.j_.trace)
<< "rippleLiquidity:" << "rippleLiquidity:"
@@ -177,14 +172,11 @@ void rippleLiquidity (
// going the other way // going the other way
Issue issue{currency, uCurIssuerID}; Issue issue{currency, uCurIssuerID};
auto numerator = mulRound ( auto numerator = mulRound (saPrv, uQualityIn, issue, true);
saPrv, uQualityIn, issue, true,
amountCalcSwitchovers);
// A part of current. All of previous. (Cur is the driver // A part of current. All of previous. (Cur is the driver
// variable.) // variable.)
STAmount saCurOut = divRound ( STAmount saCurOut = divRound (
numerator, uQualityOut, issue, true, numerator, uQualityOut, issue, true);
amountCalcSwitchovers);
JLOG (rippleCalc.j_.trace) JLOG (rippleCalc.j_.trace)
<< "rippleLiquidity:4: saCurReq=" << saCurReq; << "rippleLiquidity:4: saCurReq=" << saCurReq;

View File

@@ -147,17 +147,15 @@ public:
for (int i=0;i<101;++i) for (int i=0;i<101;++i)
env (offer (carol, USD (1), EUR (2))); env (offer (carol, USD (1), EUR (2)));
auto const switchoverTime = STAmountCalcSwitchovers::enableUnderflowFixCloseTime ();
for (auto timeDelta : { for (auto timeDelta : {
- env.closed()->info().closeTimeResolution, - env.closed()->info().closeTimeResolution,
env.closed()->info().closeTimeResolution} ) env.closed()->info().closeTimeResolution} )
{ {
auto const closeTime = switchoverTime + timeDelta; auto const closeTime = STAmountSO::soTime + timeDelta;
STAmountCalcSwitchovers switchover (closeTime);
env.close (closeTime); env.close (closeTime);
*stAmountCalcSwitchover = closeTime <= STAmountSO::soTime;
// Will fail without the underflow fix // Will fail without the underflow fix
auto expectedResult = switchover.enableUnderflowFix () ? auto expectedResult = *stAmountCalcSwitchover ?
tesSUCCESS : tecPATH_PARTIAL; tesSUCCESS : tecPATH_PARTIAL;
env (pay ("alice", "bob", EUR (epsilon)), path (~EUR), env (pay ("alice", "bob", EUR (epsilon)), path (~EUR),
sendmax (USD (100)), ter (expectedResult)); sendmax (USD (100)), ter (expectedResult));

View File

@@ -85,7 +85,7 @@ class Taker_test : public beast::unit_test::suite
{ {
/* check if composed quality should be rejected */ /* check if composed quality should be rejected */
Quality const quality (composed_quality ( Quality const quality (composed_quality (
quality1, quality2, STAmountCalcSwitchovers{false})); quality1, quality2));
if (reject (quality)) if (reject (quality))
return std::make_pair( return std::make_pair(

View File

@@ -274,8 +274,7 @@ CreateOffer::dry_offer (ApplyView& view, Offer const& offer)
std::pair<bool, Quality> std::pair<bool, Quality>
CreateOffer::select_path ( CreateOffer::select_path (
bool have_direct, OfferStream const& direct, bool have_direct, OfferStream const& direct,
bool have_bridge, OfferStream const& leg1, OfferStream const& leg2, bool have_bridge, OfferStream const& leg1, OfferStream const& leg2)
STAmountCalcSwitchovers const& amountCalcSwitchovers)
{ {
// If we don't have any viable path, why are we here?! // If we don't have any viable path, why are we here?!
assert (have_direct || have_bridge); assert (have_direct || have_bridge);
@@ -285,7 +284,7 @@ CreateOffer::select_path (
return std::make_pair (true, direct.tip ().quality ()); return std::make_pair (true, direct.tip ().quality ());
Quality const bridged_quality (composed_quality ( Quality const bridged_quality (composed_quality (
leg1.tip ().quality (), leg2.tip ().quality (), amountCalcSwitchovers)); leg1.tip ().quality (), leg2.tip ().quality ()));
if (have_direct) if (have_direct)
{ {
@@ -352,9 +351,6 @@ CreateOffer::bridged_cross (
auto viewJ = ctx_.app.journal ("View"); auto viewJ = ctx_.app.journal ("View");
STAmountCalcSwitchovers amountCalcSwitchovers (
view.info ().parentCloseTime);
// Modifying the order or logic of the operations in the loop will cause // Modifying the order or logic of the operations in the loop will cause
// a protocol breaking change. // a protocol breaking change.
while (have_direct || have_bridge) while (have_direct || have_bridge)
@@ -368,8 +364,7 @@ CreateOffer::bridged_cross (
std::tie (use_direct, quality) = select_path ( std::tie (use_direct, quality) = select_path (
have_direct, offers_direct, have_direct, offers_direct,
have_bridge, offers_leg1, offers_leg2, have_bridge, offers_leg1, offers_leg2);
amountCalcSwitchovers);
// We are always looking at the best quality; we are done with // We are always looking at the best quality; we are done with
@@ -473,7 +468,7 @@ CreateOffer::bridged_cross (
Throw<std::logic_error> ("bridged crossing: nothing was fully consumed."); Throw<std::logic_error> ("bridged crossing: nothing was fully consumed.");
} }
return std::make_pair(cross_result, taker.remaining_offer (amountCalcSwitchovers)); return std::make_pair(cross_result, taker.remaining_offer ());
} }
std::pair<TER, Amounts> std::pair<TER, Amounts>
@@ -554,8 +549,7 @@ CreateOffer::direct_cross (
Throw<std::logic_error> ("direct crossing: nothing was fully consumed."); Throw<std::logic_error> ("direct crossing: nothing was fully consumed.");
} }
STAmountCalcSwitchovers amountCalcSwitchovers (view.info ().parentCloseTime); return std::make_pair(cross_result, taker.remaining_offer ());
return std::make_pair(cross_result, taker.remaining_offer (amountCalcSwitchovers));
} }
// Step through the stream for as long as possible, skipping any offers // Step through the stream for as long as possible, skipping any offers
@@ -598,7 +592,6 @@ CreateOffer::cross (
Taker taker (cross_type_, view, account_, taker_amount, Taker taker (cross_type_, view, account_, taker_amount,
ctx_.tx.getFlags(), beast::Journal (takerSink)); ctx_.tx.getFlags(), beast::Journal (takerSink));
STAmountCalcSwitchovers amountCalcSwitchovers (view.info ().parentCloseTime);
try try
{ {
if (cross_type_ == CrossType::IouToIou) if (cross_type_ == CrossType::IouToIou)
@@ -609,7 +602,7 @@ CreateOffer::cross (
catch (std::exception const& e) catch (std::exception const& e)
{ {
j_.error << "Exception during offer crossing: " << e.what (); j_.error << "Exception during offer crossing: " << e.what ();
return std::make_pair (tecINTERNAL, taker.remaining_offer (amountCalcSwitchovers)); return std::make_pair (tecINTERNAL, taker.remaining_offer ());
} }
} }

View File

@@ -78,8 +78,7 @@ private:
std::pair<bool, Quality> std::pair<bool, Quality>
select_path ( select_path (
bool have_direct, OfferStream const& direct, bool have_direct, OfferStream const& direct,
bool have_bridge, OfferStream const& leg1, OfferStream const& leg2, bool have_bridge, OfferStream const& leg1, OfferStream const& leg2);
STAmountCalcSwitchovers const& amountCalcSwitchovers);
std::pair<TER, Amounts> std::pair<TER, Amounts>
bridged_cross ( bridged_cross (

View File

@@ -140,7 +140,7 @@ BasicTaker::done () const
} }
Amounts Amounts
BasicTaker::remaining_offer (STAmountCalcSwitchovers const& amountCalcSwitchovers) const BasicTaker::remaining_offer () const
{ {
// If the taker is done, then there's no offer to place. // If the taker is done, then there's no offer to place.
if (done ()) if (done ())
@@ -156,15 +156,14 @@ BasicTaker::remaining_offer (STAmountCalcSwitchovers const& amountCalcSwitchover
// We scale the output based on the remaining input: // We scale the output based on the remaining input:
return Amounts (remaining_.in, divRound ( return Amounts (remaining_.in, divRound (
remaining_.in, quality_.rate (), issue_out_, true, remaining_.in, quality_.rate (), issue_out_, true));
amountCalcSwitchovers));
} }
assert (remaining_.out > zero); assert (remaining_.out > zero);
// We scale the input based on the remaining output: // We scale the input based on the remaining output:
return Amounts (mulRound ( return Amounts (mulRound (
remaining_.out, quality_.rate (), issue_in_, true, amountCalcSwitchovers), remaining_.out, quality_.rate (), issue_in_, true),
remaining_.out); remaining_.out);
} }

View File

@@ -167,7 +167,7 @@ public:
It is always at the original offer quality (quality_) It is always at the original offer quality (quality_)
*/ */
Amounts Amounts
remaining_offer (STAmountCalcSwitchovers const& amountCalcSwitchovers) const; remaining_offer () const;
/** Returns the amount that the offer was originally placed at. */ /** Returns the amount that the offer was originally placed at. */
Amounts const& Amounts const&

View File

@@ -108,8 +108,8 @@ apply (Application& app, OpenView& view,
STTx const& tx, ApplyFlags flags, STTx const& tx, ApplyFlags flags,
beast::Journal j) beast::Journal j)
{ {
auto pfresult = preflight(app, view.rules(), STAmountSO saved(view.info().parentCloseTime);
tx, flags, j); auto pfresult = preflight(app, view.rules(), tx, flags, j);
auto pcresult = preclaim(pfresult, app, view); auto pcresult = preclaim(pfresult, app, view);
return doApply(pcresult, app, view); return doApply(pcresult, app, view);
} }

View File

@@ -44,7 +44,7 @@ struct LocalValues
T t_; T t_;
Value() = default; Value() = default;
Value(T const& t) : t_(t) {} explicit Value(T const& t) : t_(t) {}
void* get() override void* get() override
{ {
@@ -52,6 +52,7 @@ struct LocalValues
} }
}; };
// Keys are the address of a LocalValue.
std::unordered_map<void const*, std::unique_ptr<BasicValue>> values; std::unordered_map<void const*, std::unique_ptr<BasicValue>> values;
static static

View File

@@ -131,16 +131,14 @@ public:
to prevent money creation. to prevent money creation.
*/ */
Amounts Amounts
ceil_in (Amounts const& amount, STAmount const& limit, ceil_in (Amounts const& amount, STAmount const& limit) const;
STAmountCalcSwitchovers const& switchovers) const;
/** Returns the scaled amount with out capped. /** Returns the scaled amount with out capped.
Math is avoided if the result is exact. The input is clamped Math is avoided if the result is exact. The input is clamped
to prevent money creation. to prevent money creation.
*/ */
Amounts Amounts
ceil_out (Amounts const& amount, STAmount const& limit, ceil_out (Amounts const& amount, STAmount const& limit) const;
STAmountCalcSwitchovers const& switchovers) const;
/** Returns `true` if lhs is lower quality than `rhs`. /** Returns `true` if lhs is lower quality than `rhs`.
Lower quality means the taker receives a worse deal. Lower quality means the taker receives a worse deal.
@@ -181,8 +179,7 @@ public:
@param rhs The second leg of the path: intermediate to output. @param rhs The second leg of the path: intermediate to output.
*/ */
Quality Quality
composed_quality (Quality const& lhs, Quality const& rhs, composed_quality (Quality const& lhs, Quality const& rhs);
STAmountCalcSwitchovers const& switchovers);
} }

View File

@@ -21,6 +21,7 @@
#define RIPPLE_PROTOCOL_STAMOUNT_H_INCLUDED #define RIPPLE_PROTOCOL_STAMOUNT_H_INCLUDED
#include <ripple/basics/chrono.h> #include <ripple/basics/chrono.h>
#include <ripple/basics/LocalValue.h>
#include <ripple/protocol/SField.h> #include <ripple/protocol/SField.h>
#include <ripple/protocol/Serializer.h> #include <ripple/protocol/Serializer.h>
#include <ripple/protocol/STBase.h> #include <ripple/protocol/STBase.h>
@@ -378,32 +379,14 @@ divide (STAmount const& v1, STAmount const& v2, Issue const& issue);
STAmount STAmount
multiply (STAmount const& v1, STAmount const& v2, Issue const& issue); multiply (STAmount const& v1, STAmount const& v2, Issue const& issue);
/** Control when bugfixes that require switchover dates are enabled */
class STAmountCalcSwitchovers
{
bool enableUnderflowFix_ {false};
public:
STAmountCalcSwitchovers () = delete;
explicit
STAmountCalcSwitchovers (NetClock::time_point parentCloseTime);
explicit
STAmountCalcSwitchovers (bool enableAll)
: enableUnderflowFix_ (enableAll) {}
bool enableUnderflowFix () const;
// for tests
static NetClock::time_point enableUnderflowFixCloseTime ();
};
// multiply, or divide rounding result in specified direction // multiply, or divide rounding result in specified direction
STAmount STAmount
mulRound (STAmount const& v1, STAmount const& v2, mulRound (STAmount const& v1, STAmount const& v2,
Issue const& issue, bool roundUp, Issue const& issue, bool roundUp);
STAmountCalcSwitchovers const& switchovers);
STAmount STAmount
divRound (STAmount const& v1, STAmount const& v2, divRound (STAmount const& v1, STAmount const& v2,
Issue const& issue, bool roundUp, Issue const& issue, bool roundUp);
STAmountCalcSwitchovers const& switchovers);
// Someone is offering X for Y, what is the rate? // Someone is offering X for Y, what is the rate?
// Rate: smaller is better, the taker wants the most out: in/out // Rate: smaller is better, the taker wants the most out: in/out
@@ -418,6 +401,30 @@ inline bool isXRP(STAmount const& amount)
return isXRP (amount.issue().currency); return isXRP (amount.issue().currency);
} }
extern LocalValue<bool> stAmountCalcSwitchover;
/** RAII class to set and restore the STAmount calc switchover.*/
class STAmountSO
{
public:
explicit STAmountSO(NetClock::time_point const closeTime)
: saved_(*stAmountCalcSwitchover)
{
*stAmountCalcSwitchover = closeTime <= soTime;
}
~STAmountSO()
{
*stAmountCalcSwitchover = saved_;
}
// Mon Dec 28, 2015 10:00:00am PST
static NetClock::time_point const soTime;
private:
bool saved_;
};
} // ripple } // ripple
#endif #endif

View File

@@ -67,13 +67,12 @@ Quality::operator-- (int)
} }
Amounts Amounts
Quality::ceil_in (Amounts const& amount, STAmount const& limit, Quality::ceil_in (Amounts const& amount, STAmount const& limit) const
STAmountCalcSwitchovers const& switchovers) const
{ {
if (amount.in > limit) if (amount.in > limit)
{ {
Amounts result (limit, divRound ( Amounts result (limit, divRound (
limit, rate(), amount.out.issue (), true, switchovers)); limit, rate(), amount.out.issue (), true));
// Clamp out // Clamp out
if (result.out > amount.out) if (result.out > amount.out)
result.out = amount.out; result.out = amount.out;
@@ -85,13 +84,12 @@ Quality::ceil_in (Amounts const& amount, STAmount const& limit,
} }
Amounts Amounts
Quality::ceil_out (Amounts const& amount, STAmount const& limit, Quality::ceil_out (Amounts const& amount, STAmount const& limit) const
STAmountCalcSwitchovers const& switchovers) const
{ {
if (amount.out > limit) if (amount.out > limit)
{ {
Amounts result (mulRound ( Amounts result (mulRound (
limit, rate(), amount.in.issue (), true, switchovers), limit); limit, rate(), amount.in.issue (), true), limit);
// Clamp in // Clamp in
if (result.in > amount.in) if (result.in > amount.in)
result.in = amount.in; result.in = amount.in;
@@ -103,8 +101,7 @@ Quality::ceil_out (Amounts const& amount, STAmount const& limit,
} }
Quality Quality
composed_quality (Quality const& lhs, Quality const& rhs, composed_quality (Quality const& lhs, Quality const& rhs)
STAmountCalcSwitchovers const& switchovers)
{ {
STAmount const lhs_rate (lhs.rate ()); STAmount const lhs_rate (lhs.rate ());
assert (lhs_rate != zero); assert (lhs_rate != zero);
@@ -113,7 +110,7 @@ composed_quality (Quality const& lhs, Quality const& rhs,
assert (rhs_rate != zero); assert (rhs_rate != zero);
STAmount const rate (mulRound ( STAmount const rate (mulRound (
lhs_rate, rhs_rate, lhs_rate.issue (), true, switchovers)); lhs_rate, rhs_rate, lhs_rate.issue (), true));
std::uint64_t const stored_exponent (rate.exponent () + 100); std::uint64_t const stored_exponent (rate.exponent () + 100);
std::uint64_t const stored_mantissa (rate.mantissa()); std::uint64_t const stored_mantissa (rate.mantissa());

View File

@@ -35,6 +35,10 @@
namespace ripple { namespace ripple {
LocalValue<bool> stAmountCalcSwitchover(true);
using namespace std::chrono_literals;
const NetClock::time_point STAmountSO::soTime{504640800s};
static const std::uint64_t tenTo14 = 100000000000000ull; static const std::uint64_t tenTo14 = 100000000000000ull;
static const std::uint64_t tenTo14m1 = tenTo14 - 1; static const std::uint64_t tenTo14m1 = tenTo14 - 1;
static const std::uint64_t tenTo17 = tenTo14 * 1000; static const std::uint64_t tenTo17 = tenTo14 * 1000;
@@ -1139,9 +1143,8 @@ canonicalizeRound (bool native, std::uint64_t& value, int& offset, bool roundUp)
} }
STAmount STAmount
mulRound (STAmount const& v1, STAmount const& v2, mulRound (STAmount const& v1, STAmount const& v2, Issue const& issue,
Issue const& issue, bool roundUp, bool roundUp)
STAmountCalcSwitchovers const& switchovers)
{ {
if (v1 == zero || v2 == zero) if (v1 == zero || v2 == zero)
return {issue}; return {issue};
@@ -1205,20 +1208,20 @@ mulRound (STAmount const& v1, STAmount const& v2,
canonicalizeRound ( canonicalizeRound (
isXRP (issue), amount, offset, resultNegative != roundUp); isXRP (issue), amount, offset, resultNegative != roundUp);
STAmount result (issue, amount, offset, resultNegative); STAmount result (issue, amount, offset, resultNegative);
if (switchovers.enableUnderflowFix () && roundUp && !resultNegative && !result) // Control when bugfixes that require switchover dates are enabled
if (roundUp && !resultNegative && !result && *stAmountCalcSwitchover)
{ {
// return the smallest value above zero // return the smallest value above zero
amount = STAmount::cMinValue; amount = STAmount::cMinValue;
offset = STAmount::cMinOffset; offset = STAmount::cMinOffset;
return STAmount (issue, amount, offset, resultNegative); return STAmount(issue, amount, offset, resultNegative);
} }
return result; return result;
} }
STAmount STAmount
divRound (STAmount const& num, STAmount const& den, divRound (STAmount const& num, STAmount const& den,
Issue const& issue, bool roundUp, Issue const& issue, bool roundUp)
STAmountCalcSwitchovers const& switchovers)
{ {
if (den == zero) if (den == zero)
Throw<std::runtime_error> ("division by zero"); Throw<std::runtime_error> ("division by zero");
@@ -1265,7 +1268,8 @@ divRound (STAmount const& num, STAmount const& den,
canonicalizeRound ( canonicalizeRound (
isXRP (issue), amount, offset, resultNegative != roundUp); isXRP (issue), amount, offset, resultNegative != roundUp);
STAmount result (issue, amount, offset, resultNegative); STAmount result (issue, amount, offset, resultNegative);
if (switchovers.enableUnderflowFix () && roundUp && !resultNegative && !result) // Control when bugfixes that require switchover dates are enabled
if (roundUp && !resultNegative && !result && *stAmountCalcSwitchover)
{ {
// return the smallest value above zero // return the smallest value above zero
amount = STAmount::cMinValue; amount = STAmount::cMinValue;
@@ -1275,23 +1279,4 @@ divRound (STAmount const& num, STAmount const& den,
return result; return result;
} }
NetClock::time_point
STAmountCalcSwitchovers::enableUnderflowFixCloseTime ()
{
using namespace std::chrono_literals;
// Mon Dec 28, 2015 10:00:00am PST
return NetClock::time_point{504640800s};
}
STAmountCalcSwitchovers::STAmountCalcSwitchovers (NetClock::time_point parentCloseTime)
{
enableUnderflowFix_ = parentCloseTime > enableUnderflowFixCloseTime();
}
bool STAmountCalcSwitchovers::enableUnderflowFix () const
{
return enableUnderflowFix_;
}
} // ripple } // ripple

View File

@@ -71,7 +71,7 @@ public:
{ {
auto expect_result (amounts (in_expected, out_expected)); auto expect_result (amounts (in_expected, out_expected));
auto actual_result (q.ceil_in ( auto actual_result (q.ceil_in (
amounts (in, out), amount (limit), STAmountCalcSwitchovers{false})); amounts (in, out), amount (limit)));
expect (actual_result == expect_result); expect (actual_result == expect_result);
} }
@@ -83,7 +83,7 @@ public:
{ {
auto const expect_result (amounts (in_expected, out_expected)); auto const expect_result (amounts (in_expected, out_expected));
auto const actual_result (q.ceil_out ( auto const actual_result (q.ceil_out (
amounts (in, out), amount (limit), STAmountCalcSwitchovers{false})); amounts (in, out), amount (limit)));
expect (actual_result == expect_result); expect (actual_result == expect_result);
} }
@@ -233,7 +233,7 @@ public:
STAmount const limit ( STAmount const limit (
raw (4131113916555555, -16)); // .4131113916555555 raw (4131113916555555, -16)); // .4131113916555555
Amounts const result ( Amounts const result (
q.ceil_out (value, limit, STAmountCalcSwitchovers{false})); q.ceil_out (value, limit));
expect (result.in != zero); expect (result.in != zero);
} }
} }
@@ -277,12 +277,12 @@ public:
Quality const q31 (Amounts (amount3, amount1)); Quality const q31 (Amounts (amount3, amount1));
expect ( expect (
composed_quality (q12, q21, STAmountCalcSwitchovers{false}) == q11); composed_quality (q12, q21) == q11);
Quality const q13_31 ( Quality const q13_31 (
composed_quality (q13, q31, STAmountCalcSwitchovers{false})); composed_quality (q13, q31));
Quality const q31_13 ( Quality const q31_13 (
composed_quality (q31, q13, STAmountCalcSwitchovers{false})); composed_quality (q31, q13));
expect (q13_31 == q31_13); expect (q13_31 == q31_13);
expect (q13_31 == q11); expect (q13_31 == q11);