mirror of
https://github.com/Xahau/xahaud.git
synced 2025-12-06 17:27:52 +00:00
Return unfunded and expired offers when flow fails:
Payments do not remove unfunded and expired offers when a payment fails. However, offer crossing is now using the payment engine and needs to know what offers were found in a removable state, even on failure.
This commit is contained in:
@@ -27,11 +27,32 @@
|
||||
#include <ripple/protocol/IOUAmount.h>
|
||||
#include <ripple/protocol/XRPAmount.h>
|
||||
|
||||
#include <boost/container/flat_set.hpp>
|
||||
|
||||
#include <numeric>
|
||||
#include <sstream>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
template<class FlowResult>
|
||||
static
|
||||
auto finishFlow (PaymentSandbox& sb,
|
||||
Issue const& srcIssue, Issue const& dstIssue,
|
||||
FlowResult&& f)
|
||||
{
|
||||
path::RippleCalc::Output result;
|
||||
if (f.ter == tesSUCCESS)
|
||||
f.sandbox->apply (sb);
|
||||
else
|
||||
result.removableOffers = std::move (f.removableOffers);
|
||||
|
||||
result.setResult (f.ter);
|
||||
result.actualAmountIn = toSTAmount (f.in, srcIssue);
|
||||
result.actualAmountOut = toSTAmount (f.out, dstIssue);
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
path::RippleCalc::Output
|
||||
flow (
|
||||
PaymentSandbox& sb,
|
||||
@@ -45,7 +66,6 @@ flow (
|
||||
boost::optional<STAmount> const& sendMax,
|
||||
beast::Journal j)
|
||||
{
|
||||
path::RippleCalc::Output result;
|
||||
|
||||
Issue const srcIssue =
|
||||
sendMax ? sendMax->issue () : Issue (deliver.issue ().currency, src);
|
||||
@@ -63,6 +83,7 @@ flow (
|
||||
|
||||
if (sr.first != tesSUCCESS)
|
||||
{
|
||||
path::RippleCalc::Output result;
|
||||
result.setResult (sr.first);
|
||||
return result;
|
||||
}
|
||||
@@ -88,67 +109,40 @@ flow (
|
||||
const bool dstIsXRP = isXRP (dstIssue.currency);
|
||||
|
||||
auto const asDeliver = toAmountSpec (deliver);
|
||||
boost::optional<PaymentSandbox> strandSB;
|
||||
|
||||
// The src account may send either xrp or iou. The dst account may receive
|
||||
// either xrp or iou. Since XRP and IOU amounts are represented by different
|
||||
// types, use templates to tell `flow` about the amount types.
|
||||
if (srcIsXRP && dstIsXRP)
|
||||
{
|
||||
auto f = flow<XRPAmount, XRPAmount> (
|
||||
sb, strands, asDeliver.xrp, defaultPaths, partialPayment, limitQuality, sendMax, j);
|
||||
|
||||
if (f.ter == tesSUCCESS)
|
||||
strandSB.emplace (std::move (*f.sandbox));
|
||||
|
||||
result.setResult (f.ter);
|
||||
result.actualAmountIn = toSTAmount (f.in);
|
||||
result.actualAmountOut = toSTAmount (f.out);
|
||||
return finishFlow (sb, srcIssue, dstIssue,
|
||||
flow<XRPAmount, XRPAmount> (
|
||||
sb, strands, asDeliver.xrp, defaultPaths, partialPayment,
|
||||
limitQuality, sendMax, j));
|
||||
}
|
||||
else if (srcIsXRP && !dstIsXRP)
|
||||
|
||||
if (srcIsXRP && !dstIsXRP)
|
||||
{
|
||||
auto f = flow<XRPAmount, IOUAmount> (
|
||||
return finishFlow (sb, srcIssue, dstIssue,
|
||||
flow<XRPAmount, IOUAmount> (
|
||||
sb, strands, asDeliver.iou, defaultPaths, partialPayment,
|
||||
limitQuality, sendMax, j));
|
||||
}
|
||||
|
||||
if (!srcIsXRP && dstIsXRP)
|
||||
{
|
||||
return finishFlow (sb, srcIssue, dstIssue,
|
||||
flow<IOUAmount, XRPAmount> (
|
||||
sb, strands, asDeliver.xrp, defaultPaths, partialPayment,
|
||||
limitQuality, sendMax, j));
|
||||
}
|
||||
|
||||
assert (!srcIsXRP && !dstIsXRP);
|
||||
return finishFlow (sb, srcIssue, dstIssue,
|
||||
flow<IOUAmount, IOUAmount> (
|
||||
sb, strands, asDeliver.iou, defaultPaths, partialPayment,
|
||||
limitQuality, sendMax, j);
|
||||
limitQuality, sendMax, j));
|
||||
|
||||
if (f.ter == tesSUCCESS)
|
||||
strandSB.emplace (std::move (*f.sandbox));
|
||||
|
||||
result.setResult (f.ter);
|
||||
result.actualAmountIn = toSTAmount (f.in);
|
||||
result.actualAmountOut = toSTAmount (f.out, dstIssue);
|
||||
}
|
||||
else if (!srcIsXRP && dstIsXRP)
|
||||
{
|
||||
auto f = flow<IOUAmount, XRPAmount> (
|
||||
sb, strands, asDeliver.xrp, defaultPaths, partialPayment,
|
||||
limitQuality, sendMax, j);
|
||||
|
||||
if (f.ter == tesSUCCESS)
|
||||
strandSB.emplace (std::move (*f.sandbox));
|
||||
|
||||
result.setResult (f.ter);
|
||||
result.actualAmountIn = toSTAmount (f.in, srcIssue);
|
||||
result.actualAmountOut = toSTAmount (f.out);
|
||||
}
|
||||
else if (!srcIsXRP && !dstIsXRP)
|
||||
{
|
||||
auto f = flow<IOUAmount, IOUAmount> (
|
||||
sb, strands, asDeliver.iou, defaultPaths, partialPayment,
|
||||
limitQuality, sendMax, j);
|
||||
|
||||
if (f.ter == tesSUCCESS)
|
||||
strandSB.emplace (std::move (*f.sandbox));
|
||||
|
||||
result.setResult (f.ter);
|
||||
result.actualAmountIn = toSTAmount (f.in, srcIssue);
|
||||
result.actualAmountOut = toSTAmount (f.out, dstIssue);
|
||||
}
|
||||
|
||||
// strandSB is only valid when flow was successful
|
||||
if (strandSB)
|
||||
strandSB->apply (sb);
|
||||
return result;
|
||||
}
|
||||
|
||||
} // ripple
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace path {
|
||||
static
|
||||
TER
|
||||
deleteOffers (ApplyView& view,
|
||||
std::set<uint256> const& offers, beast::Journal j)
|
||||
boost::container::flat_set<uint256> const& offers, beast::Journal j)
|
||||
{
|
||||
for (auto& e: offers)
|
||||
if (TER r = offerDelete (view,
|
||||
@@ -100,6 +100,8 @@ RippleCalc::Output RippleCalc::rippleCalculate (
|
||||
flowV1Out.setResult (result);
|
||||
flowV1Out.actualAmountIn = rc.actualAmountIn_;
|
||||
flowV1Out.actualAmountOut = rc.actualAmountOut_;
|
||||
if (result != tesSUCCESS && !rc.permanentlyUnfundedOffers_.empty ())
|
||||
flowV1Out.removableOffers = std::move (rc.permanentlyUnfundedOffers_);
|
||||
}
|
||||
|
||||
Output flowV2Out;
|
||||
@@ -139,7 +141,7 @@ RippleCalc::Output RippleCalc::rippleCalculate (
|
||||
{
|
||||
JLOG (j.trace()) << "Exception from flow" << e.what ();
|
||||
if (!useFlowV1Output)
|
||||
throw;
|
||||
Throw();
|
||||
}
|
||||
|
||||
if (callFlowV2 && callFlowV1 &&
|
||||
@@ -281,7 +283,7 @@ TER RippleCalc::rippleCalculate ()
|
||||
getRate (saDstAmountReq_, saMaxAmountReq_) : 0;
|
||||
|
||||
// Offers that became unfunded.
|
||||
std::set<uint256> unfundedOffersFromBestPaths;
|
||||
boost::container::flat_set<uint256> unfundedOffersFromBestPaths;
|
||||
|
||||
int iPass = 0;
|
||||
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
#include <ripple/protocol/STAmount.h>
|
||||
#include <ripple/protocol/TER.h>
|
||||
|
||||
#include <boost/container/flat_set.hpp>
|
||||
|
||||
namespace ripple {
|
||||
class Config;
|
||||
namespace path {
|
||||
@@ -53,6 +55,12 @@ public:
|
||||
// The computed output amount.
|
||||
STAmount actualAmountOut;
|
||||
|
||||
// Collection of offers found expired or unfunded. When a payment
|
||||
// succeeds, unfunded and expired offers are removed. When a payment
|
||||
// fails, they are not removed. This vector contains the offers that
|
||||
// could have been removed but were not because the payment fails. It is
|
||||
// useful for offer crossing, which does remove the offers.
|
||||
boost::container::flat_set<uint256> removableOffers;
|
||||
private:
|
||||
TER calculationResult_ = temUNKNOWN;
|
||||
|
||||
@@ -105,7 +113,7 @@ public:
|
||||
// unfunded offers in a deterministic order (hence the ordered container).
|
||||
//
|
||||
// Offers that were found unfunded.
|
||||
std::set<uint256> permanentlyUnfundedOffers_;
|
||||
boost::container::flat_set<uint256> permanentlyUnfundedOffers_;
|
||||
|
||||
// First time working in reverse a funding source was mentioned. Source may
|
||||
// only be used there.
|
||||
|
||||
@@ -31,6 +31,8 @@
|
||||
#include <ripple/protocol/Quality.h>
|
||||
#include <ripple/protocol/XRPAmount.h>
|
||||
|
||||
#include <boost/container/flat_set.hpp>
|
||||
|
||||
#include <numeric>
|
||||
#include <sstream>
|
||||
|
||||
@@ -104,14 +106,14 @@ public:
|
||||
revImp (
|
||||
PaymentSandbox& sb,
|
||||
ApplyView& afView,
|
||||
std::vector<uint256>& ofrsToRm,
|
||||
boost::container::flat_set<uint256>& ofrsToRm,
|
||||
TOut const& out);
|
||||
|
||||
std::pair<TIn, TOut>
|
||||
fwdImp (
|
||||
PaymentSandbox& sb,
|
||||
ApplyView& afView,
|
||||
std::vector<uint256>& ofrsToRm,
|
||||
boost::container::flat_set<uint256>& ofrsToRm,
|
||||
TIn const& in);
|
||||
|
||||
std::pair<bool, EitherAmount>
|
||||
@@ -210,7 +212,7 @@ void limitStepOut (Quality const& ofrQ,
|
||||
*/
|
||||
template <class TAmtIn, class TAmtOut, class Callback>
|
||||
static
|
||||
std::pair<std::vector<uint256>, std::uint32_t>
|
||||
std::pair<boost::container::flat_set<uint256>, std::uint32_t>
|
||||
forEachOffer (
|
||||
PaymentSandbox& sb,
|
||||
ApplyView& afView,
|
||||
@@ -300,7 +302,7 @@ std::pair<TIn, TOut>
|
||||
BookStep<TIn, TOut>::revImp (
|
||||
PaymentSandbox& sb,
|
||||
ApplyView& afView,
|
||||
std::vector<uint256>& ofrsToRm,
|
||||
boost::container::flat_set<uint256>& ofrsToRm,
|
||||
TOut const& out)
|
||||
{
|
||||
cache_.reset ();
|
||||
@@ -358,11 +360,10 @@ BookStep<TIn, TOut>::revImp (
|
||||
auto const r = forEachOffer<TIn, TOut> (
|
||||
sb, afView, book_,
|
||||
strandSrc_, strandDst_, eachOffer, maxOffersToConsume_, j_);
|
||||
std::vector<uint256> toRm = std::move(std::get<0>(r));
|
||||
boost::container::flat_set<uint256> toRm = std::move(std::get<0>(r));
|
||||
std::uint32_t const offersConsumed = std::get<1>(r);
|
||||
ofrsToRm.reserve (ofrsToRm.size () + toRm.size ());
|
||||
for (auto& o : toRm)
|
||||
ofrsToRm.emplace_back (std::move (o));
|
||||
ofrsToRm.insert (boost::container::ordered_unique_range_t{},
|
||||
toRm.begin (), toRm.end ());
|
||||
|
||||
if (offersConsumed >= maxOffersToConsume_)
|
||||
{
|
||||
@@ -400,7 +401,7 @@ std::pair<TIn, TOut>
|
||||
BookStep<TIn, TOut>::fwdImp (
|
||||
PaymentSandbox& sb,
|
||||
ApplyView& afView,
|
||||
std::vector<uint256>& ofrsToRm,
|
||||
boost::container::flat_set<uint256>& ofrsToRm,
|
||||
TIn const& in)
|
||||
{
|
||||
assert(cache_);
|
||||
@@ -456,11 +457,10 @@ BookStep<TIn, TOut>::fwdImp (
|
||||
auto const r = forEachOffer<TIn, TOut> (
|
||||
sb, afView, book_,
|
||||
strandSrc_, strandDst_, eachOffer, maxOffersToConsume_, j_);
|
||||
std::vector<uint256> toRm = std::move(std::get<0>(r));
|
||||
boost::container::flat_set<uint256> toRm = std::move(std::get<0>(r));
|
||||
std::uint32_t const offersConsumed = std::get<1>(r);
|
||||
ofrsToRm.reserve (ofrsToRm.size () + toRm.size ());
|
||||
for (auto& o : toRm)
|
||||
ofrsToRm.emplace_back (std::move (o));
|
||||
ofrsToRm.insert (boost::container::ordered_unique_range_t{},
|
||||
toRm.begin (), toRm.end ());
|
||||
|
||||
if (offersConsumed >= maxOffersToConsume_)
|
||||
{
|
||||
@@ -510,7 +510,7 @@ BookStep<TIn, TOut>::validFwd (
|
||||
|
||||
try
|
||||
{
|
||||
std::vector<uint256> dummy;
|
||||
boost::container::flat_set<uint256> dummy;
|
||||
fwdImp (sb, afView, dummy, get<TIn> (in)); // changes cache
|
||||
}
|
||||
catch (FlowException const&)
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
#include <ripple/protocol/IOUAmount.h>
|
||||
#include <ripple/protocol/Quality.h>
|
||||
|
||||
#include <boost/container/flat_set.hpp>
|
||||
|
||||
#include <numeric>
|
||||
#include <sstream>
|
||||
|
||||
@@ -111,14 +113,14 @@ class DirectStepI : public StepImp<IOUAmount, IOUAmount, DirectStepI>
|
||||
revImp (
|
||||
PaymentSandbox& sb,
|
||||
ApplyView& afView,
|
||||
std::vector<uint256>& ofrsToRm,
|
||||
boost::container::flat_set<uint256>& ofrsToRm,
|
||||
IOUAmount const& out);
|
||||
|
||||
std::pair<IOUAmount, IOUAmount>
|
||||
fwdImp (
|
||||
PaymentSandbox& sb,
|
||||
ApplyView& afView,
|
||||
std::vector<uint256>& ofrsToRm,
|
||||
boost::container::flat_set<uint256>& ofrsToRm,
|
||||
IOUAmount const& in);
|
||||
|
||||
std::pair<bool, EitherAmount>
|
||||
@@ -195,7 +197,7 @@ std::pair<IOUAmount, IOUAmount>
|
||||
DirectStepI::revImp (
|
||||
PaymentSandbox& sb,
|
||||
ApplyView& /*afView*/,
|
||||
std::vector<uint256>& /*ofrsToRm*/,
|
||||
boost::container::flat_set<uint256>& /*ofrsToRm*/,
|
||||
IOUAmount const& out)
|
||||
{
|
||||
cache_.reset ();
|
||||
@@ -313,7 +315,7 @@ std::pair<IOUAmount, IOUAmount>
|
||||
DirectStepI::fwdImp (
|
||||
PaymentSandbox& sb,
|
||||
ApplyView& /*afView*/,
|
||||
std::vector<uint256>& /*ofrsToRm*/,
|
||||
boost::container::flat_set<uint256>& /*ofrsToRm*/,
|
||||
IOUAmount const& in)
|
||||
{
|
||||
assert (cache_);
|
||||
@@ -409,7 +411,7 @@ DirectStepI::validFwd (
|
||||
|
||||
try
|
||||
{
|
||||
std::vector<uint256> dummy;
|
||||
boost::container::flat_set<uint256> dummy;
|
||||
fwdImp (sb, afView, dummy, in.iou); // changes cache
|
||||
}
|
||||
catch (FlowException const&)
|
||||
|
||||
@@ -79,7 +79,7 @@ class Step
|
||||
rev (
|
||||
PaymentSandbox& sb,
|
||||
ApplyView& afView,
|
||||
std::vector<uint256>& ofrsToRm,
|
||||
boost::container::flat_set<uint256>& ofrsToRm,
|
||||
EitherAmount const& out) = 0;
|
||||
|
||||
/**
|
||||
@@ -98,7 +98,7 @@ class Step
|
||||
fwd (
|
||||
PaymentSandbox&,
|
||||
ApplyView& afView,
|
||||
std::vector<uint256>& ofrsToRm,
|
||||
boost::container::flat_set<uint256>& ofrsToRm,
|
||||
EitherAmount const& in) = 0;
|
||||
|
||||
virtual
|
||||
@@ -238,7 +238,7 @@ struct StepImp : public Step
|
||||
rev (
|
||||
PaymentSandbox& sb,
|
||||
ApplyView& afView,
|
||||
std::vector<uint256>& ofrsToRm,
|
||||
boost::container::flat_set<uint256>& ofrsToRm,
|
||||
EitherAmount const& out) override
|
||||
{
|
||||
auto const r =
|
||||
@@ -252,7 +252,7 @@ struct StepImp : public Step
|
||||
fwd (
|
||||
PaymentSandbox& sb,
|
||||
ApplyView& afView,
|
||||
std::vector<uint256>& ofrsToRm,
|
||||
boost::container::flat_set<uint256>& ofrsToRm,
|
||||
EitherAmount const& in) override
|
||||
{
|
||||
auto const r =
|
||||
|
||||
@@ -31,6 +31,8 @@
|
||||
|
||||
#include <boost/container/flat_set.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <numeric>
|
||||
#include <sstream>
|
||||
|
||||
@@ -43,14 +45,14 @@ struct StrandResult
|
||||
TInAmt in = beast::zero;
|
||||
TOutAmt out = beast::zero;
|
||||
boost::optional<PaymentSandbox> sandbox;
|
||||
std::vector<uint256> ofrsToRm; // offers to remove
|
||||
boost::container::flat_set<uint256> ofrsToRm; // offers to remove
|
||||
|
||||
StrandResult () = default;
|
||||
|
||||
StrandResult (TInAmt const& in_,
|
||||
TOutAmt const& out_,
|
||||
PaymentSandbox&& sandbox_,
|
||||
std::vector<uint256> ofrsToRm_)
|
||||
boost::container::flat_set<uint256> ofrsToRm_)
|
||||
: ter (tesSUCCESS)
|
||||
, in (in_)
|
||||
, out (out_)
|
||||
@@ -59,7 +61,7 @@ struct StrandResult
|
||||
{
|
||||
}
|
||||
|
||||
StrandResult (TER ter_, std::vector<uint256> ofrsToRm_)
|
||||
StrandResult (TER ter_, boost::container::flat_set<uint256> ofrsToRm_)
|
||||
: ter (ter_), ofrsToRm (std::move (ofrsToRm_))
|
||||
{
|
||||
}
|
||||
@@ -92,7 +94,7 @@ flow (
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<uint256> ofrsToRm;
|
||||
boost::container::flat_set<uint256> ofrsToRm;
|
||||
|
||||
if (isDirectXrpToXrp<TInAmt, TOutAmt> (strand))
|
||||
{
|
||||
@@ -222,28 +224,36 @@ struct FlowResult
|
||||
TInAmt in = beast::zero;
|
||||
TOutAmt out = beast::zero;
|
||||
boost::optional<PaymentSandbox> sandbox;
|
||||
boost::container::flat_set<uint256> removableOffers;
|
||||
TER ter = temUNKNOWN;
|
||||
|
||||
FlowResult () = default;
|
||||
|
||||
FlowResult (TInAmt const& in_,
|
||||
TOutAmt const& out_,
|
||||
PaymentSandbox&& sandbox_)
|
||||
PaymentSandbox&& sandbox_,
|
||||
boost::container::flat_set<uint256> ofrsToRm)
|
||||
: in (in_)
|
||||
, out (out_)
|
||||
, sandbox (std::move (sandbox_))
|
||||
, removableOffers(std::move (ofrsToRm))
|
||||
, ter (tesSUCCESS)
|
||||
{
|
||||
}
|
||||
|
||||
FlowResult (TER ter_)
|
||||
: ter (ter_)
|
||||
FlowResult (TER ter_, boost::container::flat_set<uint256> ofrsToRm)
|
||||
: removableOffers(std::move (ofrsToRm))
|
||||
, ter (ter_)
|
||||
{
|
||||
}
|
||||
|
||||
FlowResult (TER ter_, TInAmt const& in_, TOutAmt const& out_)
|
||||
FlowResult (TER ter_,
|
||||
TInAmt const& in_,
|
||||
TOutAmt const& out_,
|
||||
boost::container::flat_set<uint256> ofrsToRm)
|
||||
: in (in_)
|
||||
, out (out_)
|
||||
, removableOffers (std::move (ofrsToRm))
|
||||
, ter (ter_)
|
||||
{
|
||||
}
|
||||
@@ -395,18 +405,22 @@ flow (PaymentSandbox const& baseView,
|
||||
return std::accumulate (col.begin () + 1, col.end (), *col.begin ());
|
||||
};
|
||||
|
||||
// These offers only need to be removed if the payment is not
|
||||
// successful
|
||||
boost::container::flat_set<uint256> ofrsToRmOnFail;
|
||||
|
||||
while (remainingOut > beast::zero &&
|
||||
(!remainingIn || *remainingIn > beast::zero))
|
||||
{
|
||||
++curTry;
|
||||
if (curTry >= maxTries)
|
||||
{
|
||||
return Result (telFAILED_PROCESSING);
|
||||
return {telFAILED_PROCESSING, std::move(ofrsToRmOnFail)};
|
||||
}
|
||||
|
||||
activeStrands.activateNext();
|
||||
|
||||
std::set<uint256> ofrsToRm;
|
||||
boost::container::flat_set<uint256> ofrsToRm;
|
||||
boost::optional<BestStrand> best;
|
||||
for (auto strand : activeStrands)
|
||||
{
|
||||
@@ -414,7 +428,8 @@ flow (PaymentSandbox const& baseView,
|
||||
sb, *strand, remainingIn, remainingOut, j);
|
||||
|
||||
// rm bad offers even if the strand fails
|
||||
ofrsToRm.insert (f.ofrsToRm.begin (), f.ofrsToRm.end ());
|
||||
ofrsToRm.insert (boost::container::ordered_unique_range_t{},
|
||||
f.ofrsToRm.begin (), f.ofrsToRm.end ());
|
||||
|
||||
if (f.ter != tesSUCCESS || f.out == beast::zero)
|
||||
continue;
|
||||
@@ -469,9 +484,16 @@ flow (PaymentSandbox const& baseView,
|
||||
|
||||
best.reset (); // view in best must be destroyed before modifying base
|
||||
// view
|
||||
for (auto const& o : ofrsToRm)
|
||||
if (auto ok = sb.peek (keylet::offer (o)))
|
||||
offerDelete (sb, ok, j);
|
||||
if (!ofrsToRm.empty ())
|
||||
{
|
||||
ofrsToRmOnFail.insert (boost::container::ordered_unique_range_t{},
|
||||
ofrsToRm.begin (), ofrsToRm.end ());
|
||||
for (auto const& o : ofrsToRm)
|
||||
{
|
||||
if (auto ok = sb.peek (keylet::offer (o)))
|
||||
offerDelete (sb, ok, j);
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldBreak)
|
||||
break;
|
||||
@@ -489,19 +511,19 @@ flow (PaymentSandbox const& baseView,
|
||||
if (actualOut > outReq)
|
||||
{
|
||||
assert (0);
|
||||
return {tefEXCEPTION};
|
||||
return {tefEXCEPTION, std::move(ofrsToRmOnFail)};
|
||||
}
|
||||
if (!partialPayment)
|
||||
{
|
||||
return {tecPATH_PARTIAL, actualIn, actualOut};
|
||||
return {tecPATH_PARTIAL, actualIn, actualOut, std::move(ofrsToRmOnFail)};
|
||||
}
|
||||
else if (actualOut == beast::zero)
|
||||
{
|
||||
return {tecPATH_DRY};
|
||||
return {tecPATH_DRY, std::move(ofrsToRmOnFail)};
|
||||
}
|
||||
}
|
||||
|
||||
return Result (actualIn, actualOut, std::move (sb));
|
||||
return Result (actualIn, actualOut, std::move (sb), std::move(ofrsToRmOnFail));
|
||||
}
|
||||
|
||||
} // ripple
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include <ripple/protocol/Quality.h>
|
||||
#include <ripple/protocol/XRPAmount.h>
|
||||
|
||||
#include <boost/container/flat_set.hpp>
|
||||
|
||||
#include <numeric>
|
||||
#include <sstream>
|
||||
@@ -83,14 +84,14 @@ class XRPEndpointStep : public StepImp<XRPAmount, XRPAmount, XRPEndpointStep>
|
||||
revImp (
|
||||
PaymentSandbox& sb,
|
||||
ApplyView& afView,
|
||||
std::vector<uint256>& ofrsToRm,
|
||||
boost::container::flat_set<uint256>& ofrsToRm,
|
||||
XRPAmount const& out);
|
||||
|
||||
std::pair<XRPAmount, XRPAmount>
|
||||
fwdImp (
|
||||
PaymentSandbox& sb,
|
||||
ApplyView& afView,
|
||||
std::vector<uint256>& ofrsToRm,
|
||||
boost::container::flat_set<uint256>& ofrsToRm,
|
||||
XRPAmount const& in);
|
||||
|
||||
std::pair<bool, EitherAmount>
|
||||
@@ -154,7 +155,7 @@ std::pair<XRPAmount, XRPAmount>
|
||||
XRPEndpointStep::revImp (
|
||||
PaymentSandbox& sb,
|
||||
ApplyView& afView,
|
||||
std::vector<uint256>& ofrsToRm,
|
||||
boost::container::flat_set<uint256>& ofrsToRm,
|
||||
XRPAmount const& out)
|
||||
{
|
||||
auto const balance = xrpLiquid (sb, acc_);
|
||||
@@ -174,7 +175,7 @@ std::pair<XRPAmount, XRPAmount>
|
||||
XRPEndpointStep::fwdImp (
|
||||
PaymentSandbox& sb,
|
||||
ApplyView& afView,
|
||||
std::vector<uint256>& ofrsToRm,
|
||||
boost::container::flat_set<uint256>& ofrsToRm,
|
||||
XRPAmount const& in)
|
||||
{
|
||||
assert (cache_);
|
||||
|
||||
@@ -17,9 +17,12 @@
|
||||
|
||||
#include <BeastConfig.h>
|
||||
#include <ripple/test/jtx.h>
|
||||
#include <ripple/app/paths/Flow.h>
|
||||
#include <ripple/app/paths/impl/Steps.h>
|
||||
#include <ripple/basics/contract.h>
|
||||
#include <ripple/core/Config.h>
|
||||
#include <ripple/ledger/PaymentSandbox.h>
|
||||
#include <ripple/ledger/Sandbox.h>
|
||||
#include <ripple/ledger/tests/PathSet.h>
|
||||
#include <ripple/protocol/Feature.h>
|
||||
#include <ripple/protocol/JsonFields.h>
|
||||
@@ -132,6 +135,28 @@ bool equal (Strand const& strand, Args&&... args)
|
||||
|
||||
struct Flow_test : public beast::unit_test::suite
|
||||
{
|
||||
// Account path element
|
||||
static auto APE(AccountID const& a)
|
||||
{
|
||||
return STPathElement (
|
||||
STPathElement::typeAccount, a, xrpCurrency (), xrpAccount ());
|
||||
};
|
||||
|
||||
// Issue path element
|
||||
static auto IPE(Issue const& iss)
|
||||
{
|
||||
return STPathElement (
|
||||
STPathElement::typeCurrency | STPathElement::typeIssuer,
|
||||
xrpAccount (), iss.currency, iss.account);
|
||||
};
|
||||
|
||||
// Currency path element
|
||||
static auto CPE(Currency const& c)
|
||||
{
|
||||
return STPathElement (
|
||||
STPathElement::typeCurrency, xrpAccount (), c, xrpAccount ());
|
||||
};
|
||||
|
||||
void testToStrand ()
|
||||
{
|
||||
testcase ("To Strand");
|
||||
@@ -152,28 +177,6 @@ struct Flow_test : public beast::unit_test::suite
|
||||
using B = ripple::Book;
|
||||
using XRPS = XRPEndpointStepInfo;
|
||||
|
||||
// Account path element
|
||||
auto APE = [](AccountID const& a)
|
||||
{
|
||||
return STPathElement (
|
||||
STPathElement::typeAccount, a, xrpCurrency (), xrpAccount ());
|
||||
};
|
||||
|
||||
// Issue path element
|
||||
auto IPE = [](Issue const& iss)
|
||||
{
|
||||
return STPathElement (
|
||||
STPathElement::typeCurrency | STPathElement::typeIssuer,
|
||||
xrpAccount (), iss.currency, iss.account);
|
||||
};
|
||||
|
||||
// Currency path element
|
||||
auto CPE = [](Currency const& c)
|
||||
{
|
||||
return STPathElement (
|
||||
STPathElement::typeCurrency, xrpAccount (), c, xrpAccount ());
|
||||
};
|
||||
|
||||
auto test = [&, this](jtx::Env& env, Issue const& deliver,
|
||||
boost::optional<Issue> const& sendMaxIssue, STPath const& path,
|
||||
TER expTer, auto&&... expSteps)
|
||||
@@ -556,7 +559,7 @@ struct Flow_test : public beast::unit_test::suite
|
||||
expect (!isOffer (env, bob, USD (50), XRP (50)));
|
||||
}
|
||||
{
|
||||
// test unfunded offers are removed
|
||||
// test unfunded offers are removed when payment succeeds
|
||||
Env env (*this, features(featureFlowV2));
|
||||
|
||||
env.fund (XRP (10000), alice, bob, carol, gw);
|
||||
@@ -594,6 +597,74 @@ struct Flow_test : public beast::unit_test::suite
|
||||
// unfunded, but should not yet be found unfunded
|
||||
expect (isOffer (env, bob, EUR (50), USD (50)));
|
||||
}
|
||||
{
|
||||
// test unfunded offers are returned when the payment fails.
|
||||
// bob makes two offers: a funded 50 USD for 50 BTC and an unfunded 50
|
||||
// EUR for 60 BTC. alice pays carol 61 USD with 61 BTC. alice only
|
||||
// has 60 BTC, so the payment will fail. The payment uses two paths:
|
||||
// one through bob's funded offer and one through his unfunded
|
||||
// offer. When the payment fails `flow` should return the unfunded
|
||||
// offer. This test is intentionally similar to the one that removes
|
||||
// unfunded offers when the payment succeeds.
|
||||
Env env (*this, features(featureFlowV2));
|
||||
|
||||
env.fund (XRP (10000), alice, bob, carol, gw);
|
||||
env.trust (USD (1000), alice, bob, carol);
|
||||
env.trust (BTC (1000), alice, bob, carol);
|
||||
env.trust (EUR (1000), alice, bob, carol);
|
||||
|
||||
env (pay (gw, alice, BTC (60)));
|
||||
env (pay (gw, bob, USD (50)));
|
||||
env (pay (gw, bob, EUR (50)));
|
||||
|
||||
env (offer (bob, BTC (50), USD (50)));
|
||||
env (offer (bob, BTC (60), EUR (50)));
|
||||
env (offer (bob, EUR (50), USD (50)));
|
||||
|
||||
// unfund offer
|
||||
env (pay (bob, gw, EUR (50)));
|
||||
expect (isOffer (env, bob, BTC (50), USD (50)));
|
||||
expect (isOffer (env, bob, BTC (60), EUR (50)));
|
||||
|
||||
auto flowJournal = env.app ().logs ().journal ("Flow");
|
||||
auto const flowResult = [&]
|
||||
{
|
||||
STAmount deliver (USD (51));
|
||||
STAmount smax (BTC (61));
|
||||
PaymentSandbox sb (env.current ().get (), tapNONE);
|
||||
STPathSet paths;
|
||||
{
|
||||
// BTC -> USD
|
||||
STPath p1 ({IPE (USD.issue ())});
|
||||
paths.push_back (p1);
|
||||
// BTC -> EUR -> USD
|
||||
STPath p2 ({IPE (EUR.issue ()), IPE (USD.issue ())});
|
||||
paths.push_back (p2);
|
||||
}
|
||||
|
||||
return flow (sb, deliver, alice, carol, paths, false, false,
|
||||
boost::none, smax, flowJournal);
|
||||
}();
|
||||
|
||||
expect (flowResult.removableOffers.size () == 1);
|
||||
env.app ().openLedger ().modify (
|
||||
[&](OpenView& view, beast::Journal j)
|
||||
{
|
||||
if (flowResult.removableOffers.empty())
|
||||
return false;
|
||||
Sandbox sb (&view, tapNONE);
|
||||
for (auto const& o : flowResult.removableOffers)
|
||||
if (auto ok = sb.peek (keylet::offer (o)))
|
||||
offerDelete (sb, ok, flowJournal);
|
||||
sb.apply (view);
|
||||
return true;
|
||||
});
|
||||
|
||||
// used in payment, but since payment failed should be untouched
|
||||
expect (isOffer (env, bob, BTC (50), USD (50)));
|
||||
// found unfunded
|
||||
expect (!isOffer (env, bob, BTC (60), EUR (50)));
|
||||
}
|
||||
}
|
||||
|
||||
void testTransferRate ()
|
||||
|
||||
@@ -216,7 +216,7 @@ OfferStream::permRmOffer (std::shared_ptr<SLE> const& sle)
|
||||
template<class TIn, class TOut>
|
||||
void FlowOfferStream<TIn, TOut>::permRmOffer (std::shared_ptr<SLE> const& sle)
|
||||
{
|
||||
toRemove_.push_back (sle->key());
|
||||
toRemove_.insert (sle->key());
|
||||
}
|
||||
|
||||
template class FlowOfferStream<STAmount, STAmount>;
|
||||
|
||||
@@ -28,6 +28,8 @@
|
||||
#include <ripple/protocol/Quality.h>
|
||||
#include <beast/utility/Journal.h>
|
||||
|
||||
#include <boost/container/flat_set.hpp>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
template<class TIn, class TOut>
|
||||
@@ -163,7 +165,7 @@ template <class TIn, class TOut>
|
||||
class FlowOfferStream : public TOfferStreamBase<TIn, TOut>
|
||||
{
|
||||
private:
|
||||
std::vector<uint256> toRemove_;
|
||||
boost::container::flat_set<uint256> toRemove_;
|
||||
protected:
|
||||
void
|
||||
permRmOffer (std::shared_ptr<SLE> const& sle) override;
|
||||
@@ -171,7 +173,7 @@ protected:
|
||||
public:
|
||||
using TOfferStreamBase<TIn, TOut>::TOfferStreamBase;
|
||||
|
||||
std::vector<uint256> const& toRemove () const
|
||||
boost::container::flat_set<uint256> const& toRemove () const
|
||||
{
|
||||
return toRemove_;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user