mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-24 13:05:53 +00:00
Convert STAmount switchovers to tls (RIPD-1068)
This commit is contained in:
committed by
Edward Hennis
parent
4fb6bf3e67
commit
14dde47173
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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));
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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 ());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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 (
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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&
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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());
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
Reference in New Issue
Block a user