rippled
Loading...
Searching...
No Matches
StrandFlow.h
1#pragma once
2
3#include <xrpl/basics/Log.h>
4#include <xrpl/ledger/View.h>
5#include <xrpl/ledger/helpers/OfferHelpers.h>
6#include <xrpl/ledger/helpers/RippleStateHelpers.h>
7#include <xrpl/protocol/Feature.h>
8#include <xrpl/protocol/IOUAmount.h>
9#include <xrpl/protocol/XRPAmount.h>
10#include <xrpl/tx/paths/Flow.h>
11#include <xrpl/tx/paths/detail/AmountSpec.h>
12#include <xrpl/tx/paths/detail/FlatSets.h>
13#include <xrpl/tx/paths/detail/FlowDebugInfo.h>
14#include <xrpl/tx/paths/detail/Steps.h>
15#include <xrpl/tx/transactors/dex/AMMContext.h>
16#include <xrpl/tx/transactors/dex/AMMHelpers.h>
17
18#include <boost/container/flat_set.hpp>
19
20#include <algorithm>
21#include <iterator>
22#include <numeric>
23
24namespace xrpl {
25
27template <class TInAmt, class TOutAmt>
29{
30 bool success = false;
31 TInAmt in = beast::zero;
32 TOutAmt out = beast::zero;
34 boost::container::flat_set<uint256> ofrsToRm;
35 // Num offers consumed or partially consumed (includes expired and unfunded
36 // offers)
38 // strand can be inactive if there is no more liquidity or too many offers
39 // have been consumed
40 bool inactive = false;
42
44 StrandResult() = default;
45
47 Strand const& strand,
48 TInAmt const& in_,
49 TOutAmt const& out_,
50 PaymentSandbox&& sandbox_,
51 boost::container::flat_set<uint256> ofrsToRm_,
52 bool inactive_)
53 : success(true)
54 , in(in_)
55 , out(out_)
56 , sandbox(std::move(sandbox_))
57 , ofrsToRm(std::move(ofrsToRm_))
58 , ofrsUsed(offersUsed(strand))
59 , inactive(inactive_)
60 {
61 }
62
63 StrandResult(Strand const& strand, boost::container::flat_set<uint256> ofrsToRm_)
64 : ofrsToRm(std::move(ofrsToRm_)), ofrsUsed(offersUsed(strand))
65 {
66 }
67};
68
80template <class TInAmt, class TOutAmt>
81StrandResult<TInAmt, TOutAmt>
83 PaymentSandbox const& baseView,
84 Strand const& strand,
85 std::optional<TInAmt> const& maxIn,
86 TOutAmt const& out,
88{
90 if (strand.empty())
91 {
92 JLOG(j.warn()) << "Empty strand passed to Liquidity";
93 return {};
94 }
95
96 boost::container::flat_set<uint256> ofrsToRm;
97
98 if (isDirectXrpToXrp<TInAmt, TOutAmt>(strand))
99 {
100 return Result{strand, std::move(ofrsToRm)};
101 }
102
103 try
104 {
105 std::size_t const s = strand.size();
106
107 std::size_t limitingStep = strand.size();
108 std::optional<PaymentSandbox> sb(&baseView);
109 // The "all funds" view determines if an offer becomes unfunded or is
110 // found unfunded
111 // These are the account balances before the strand executes
112 std::optional<PaymentSandbox> afView(&baseView);
114 {
115 EitherAmount stepOut(out);
116 for (auto i = s; i--;)
117 {
118 auto r = strand[i]->rev(*sb, *afView, ofrsToRm, stepOut);
119 if (strand[i]->isZero(r.second))
120 {
121 JLOG(j.trace()) << "Strand found dry in rev";
122 return Result{strand, std::move(ofrsToRm)};
123 }
124
125 if (i == 0 && maxIn && *maxIn < get<TInAmt>(r.first))
126 {
127 // limiting - exceeded maxIn
128 // Throw out previous results
129 sb.emplace(&baseView);
130 limitingStep = i;
131
132 // re-execute the limiting step
133 r = strand[i]->fwd(*sb, *afView, ofrsToRm, EitherAmount(*maxIn));
134 limitStepOut = r.second;
135
136 if (strand[i]->isZero(r.second))
137 {
138 JLOG(j.trace()) << "First step found dry";
139 return Result{strand, std::move(ofrsToRm)};
140 }
141 if (get<TInAmt>(r.first) != *maxIn)
142 {
143 // Something is very wrong
144 // throwing out the sandbox can only increase liquidity
145 // yet the limiting is still limiting
146 // LCOV_EXCL_START
147 JLOG(j.fatal())
148 << "Re-executed limiting step failed. r.first: "
149 << to_string(get<TInAmt>(r.first)) << " maxIn: " << to_string(*maxIn);
150 UNREACHABLE(
151 "xrpl::flow : first step re-executing the "
152 "limiting step failed");
153 return Result{strand, std::move(ofrsToRm)};
154 // LCOV_EXCL_STOP
155 }
156 }
157 else if (!strand[i]->equalOut(r.second, stepOut))
158 {
159 // limiting
160 // Throw out previous results
161 sb.emplace(&baseView);
162 afView.emplace(&baseView);
163 limitingStep = i;
164
165 // re-execute the limiting step
166 stepOut = r.second;
167 r = strand[i]->rev(*sb, *afView, ofrsToRm, stepOut);
168 limitStepOut = r.second;
169
170 if (strand[i]->isZero(r.second))
171 {
172 // A tiny input amount can cause this step to output
173 // zero. I.e. 10^-80 IOU into an IOU -> XRP offer.
174 JLOG(j.trace()) << "Limiting step found dry";
175 return Result{strand, std::move(ofrsToRm)};
176 }
177 if (!strand[i]->equalOut(r.second, stepOut))
178 {
179 // Something is very wrong
180 // throwing out the sandbox can only increase liquidity
181 // yet the limiting is still limiting
182 // LCOV_EXCL_START
183#ifndef NDEBUG
184 JLOG(j.fatal())
185 << "Re-executed limiting step failed. r.second: " << r.second
186 << " stepOut: " << stepOut;
187#else
188 JLOG(j.fatal()) << "Re-executed limiting step failed";
189#endif
190 UNREACHABLE(
191 "xrpl::flow : limiting step re-executing the "
192 "limiting step failed");
193 return Result{strand, std::move(ofrsToRm)};
194 // LCOV_EXCL_STOP
195 }
196 }
197
198 // prev node needs to produce what this node wants to consume
199 stepOut = r.first;
200 }
201 }
202
203 {
205 for (auto i = limitingStep + 1; i < s; ++i)
206 {
207 auto const r = strand[i]->fwd(*sb, *afView, ofrsToRm, stepIn);
208 if (strand[i]->isZero(r.second))
209 {
210 // A tiny input amount can cause this step to output zero.
211 // I.e. 10^-80 IOU into an IOU -> XRP offer.
212 JLOG(j.trace()) << "Non-limiting step found dry";
213 return Result{strand, std::move(ofrsToRm)};
214 }
215 if (!strand[i]->equalIn(r.first, stepIn))
216 {
217 // The limits should already have been found, so executing a
218 // strand forward from the limiting step should not find a
219 // new limit
220 // LCOV_EXCL_START
221#ifndef NDEBUG
222 JLOG(j.fatal()) << "Re-executed forward pass failed. r.first: " << r.first
223 << " stepIn: " << stepIn;
224#else
225 JLOG(j.fatal()) << "Re-executed forward pass failed";
226#endif
227 UNREACHABLE(
228 "xrpl::flow : non-limiting step re-executing the "
229 "forward pass failed");
230 return Result{strand, std::move(ofrsToRm)};
231 // LCOV_EXCL_STOP
232 }
233 stepIn = r.second;
234 }
235 }
236
237 auto const strandIn = *strand.front()->cachedIn();
238 auto const strandOut = *strand.back()->cachedOut();
239
240#ifndef NDEBUG
241 {
242 // Check that the strand will execute as intended
243 // Re-executing the strand will change the cached values
244 PaymentSandbox checkSB(&baseView);
245 PaymentSandbox checkAfView(&baseView);
246 EitherAmount stepIn(*strand[0]->cachedIn());
247 for (auto i = 0; i < s; ++i)
248 {
249 bool valid;
250 std::tie(valid, stepIn) = strand[i]->validFwd(checkSB, checkAfView, stepIn);
251 if (!valid)
252 {
253 JLOG(j.warn()) << "Strand re-execute check failed. Step: " << i;
254 break;
255 }
256 }
257 }
258#endif
259
260 bool const inactive =
261 std::any_of(strand.begin(), strand.end(), [](std::unique_ptr<Step> const& step) {
262 return step->inactive();
263 });
264
265 return Result(
266 strand,
267 get<TInAmt>(strandIn),
268 get<TOutAmt>(strandOut),
269 std::move(*sb),
270 std::move(ofrsToRm),
271 inactive);
272 }
273 catch (FlowException const&)
274 {
275 return Result{strand, std::move(ofrsToRm)};
276 }
277}
278
280template <class TInAmt, class TOutAmt>
281struct FlowResult
282{
283 TInAmt in = beast::zero;
284 TOutAmt out = beast::zero;
286 boost::container::flat_set<uint256> removableOffers;
287 TER ter = temUNKNOWN;
288
289 FlowResult() = default;
290
291 FlowResult(
292 TInAmt const& in_,
293 TOutAmt const& out_,
294 PaymentSandbox&& sandbox_,
295 boost::container::flat_set<uint256> ofrsToRm)
296 : in(in_)
297 , out(out_)
298 , sandbox(std::move(sandbox_))
299 , removableOffers(std::move(ofrsToRm))
300 , ter(tesSUCCESS)
301 {
302 }
303
304 FlowResult(TER ter_, boost::container::flat_set<uint256> ofrsToRm)
305 : removableOffers(std::move(ofrsToRm)), ter(ter_)
306 {
307 }
308
309 FlowResult(
310 TER ter_,
311 TInAmt const& in_,
312 TOutAmt const& out_,
313 boost::container::flat_set<uint256> ofrsToRm)
314 : in(in_), out(out_), removableOffers(std::move(ofrsToRm)), ter(ter_)
315 {
316 }
317};
319
322qualityUpperBound(ReadView const& v, Strand const& strand)
323{
324 Quality q{STAmount::uRateOne};
327 for (auto const& step : strand)
328 {
329 if (std::tie(stepQ, dir) = step->qualityUpperBound(v, dir); stepQ)
330 q = composed_quality(q, *stepQ);
331 else
332 return std::nullopt;
333 }
334 return q;
335};
337
339
347template <typename TOutAmt>
348inline TOutAmt
349limitOut(
350 ReadView const& v,
351 Strand const& strand,
352 TOutAmt const& remainingOut,
353 Quality const& limitQuality)
354{
355 std::optional<QualityFunction> stepQualityFunc;
358 for (auto const& step : strand)
359 {
360 if (std::tie(stepQualityFunc, dir) = step->getQualityFunc(v, dir); stepQualityFunc)
361 {
362 if (!qf)
363 qf = stepQualityFunc;
364 else
365 qf->combine(*stepQualityFunc);
366 }
367 else
368 return remainingOut;
369 }
370
371 // QualityFunction is constant
372 if (!qf || qf->isConst())
373 return remainingOut;
374
375 auto const out = [&]() {
376 if (auto const out = qf->outFromAvgQ(limitQuality); !out)
377 return remainingOut;
378 else if constexpr (std::is_same_v<TOutAmt, XRPAmount>)
379 return XRPAmount{*out};
380 else if constexpr (std::is_same_v<TOutAmt, IOUAmount>)
381 return IOUAmount{*out};
382 else
383 return STAmount{remainingOut.issue(), out->mantissa(), out->exponent()};
384 }();
385 // A tiny difference could be due to the round off
386 if (withinRelativeDistance(out, remainingOut, Number(1, -9)))
387 return remainingOut;
388 return std::min(out, remainingOut);
389};
391
393/* Track the non-dry strands
394
395 flow will search the non-dry strands (stored in `cur_`) for the best
396 available liquidity If flow doesn't use all the liquidity of a strand, that
397 strand is added to `next_`. The strands in `next_` are searched after the
398 current best liquidity is used.
399 */
400class ActiveStrands
401{
402private:
403 // Strands to be explored for liquidity
405 // Strands that may be explored for liquidity on the next iteration
407
408public:
409 ActiveStrands(std::vector<Strand> const& strands)
410 {
411 cur_.reserve(strands.size());
412 next_.reserve(strands.size());
413 for (auto& strand : strands)
414 next_.push_back(&strand);
415 }
416
417 // Start a new iteration in the search for liquidity
418 // Set the current strands to the strands in `next_`
419 void
420 activateNext(ReadView const& v, std::optional<Quality> const& limitQuality)
421 {
422 // add the strands in `next_` to `cur_`, sorted by theoretical quality.
423 // Best quality first.
424 cur_.clear();
425 if (!next_.empty())
426 {
428 strandQualities.reserve(next_.size());
429 if (next_.size() > 1) // no need to sort one strand
430 {
431 for (Strand const* strand : next_)
432 {
433 if (!strand)
434 {
435 // should not happen
436 continue;
437 }
438 if (auto const qual = qualityUpperBound(v, *strand))
439 {
440 if (limitQuality && *qual < *limitQuality)
441 {
442 // If a strand's quality is ever over limitQuality
443 // it is no longer part of the candidate set. Note
444 // that when transfer fees are charged, and an
445 // account goes from redeeming to issuing then
446 // strand quality _can_ increase; However, this is
447 // an unusual corner case.
448 continue;
449 }
450 strandQualities.push_back({*qual, strand});
451 }
452 }
453 // must stable sort for deterministic order across different c++
454 // standard library implementations
456 strandQualities.begin(),
457 strandQualities.end(),
458 [](auto const& lhs, auto const& rhs) {
459 // higher qualities first
460 return std::get<Quality>(lhs) > std::get<Quality>(rhs);
461 });
462 next_.clear();
463 next_.reserve(strandQualities.size());
464 for (auto const& sq : strandQualities)
465 {
467 }
468 }
469 }
470 std::swap(cur_, next_);
471 }
472
473 Strand const*
474 get(size_t i) const
475 {
476 if (i >= cur_.size())
477 {
478 // LCOV_EXCL_START
479 UNREACHABLE("xrpl::ActiveStrands::get : input out of range");
480 return nullptr;
481 // LCOV_EXCL_STOP
482 }
483 return cur_[i];
484 }
485
486 void
487 push(Strand const* s)
488 {
489 next_.push_back(s);
490 }
491
492 // Push the strands from index i to the end of cur_ to next_
493 void
494 pushRemainingCurToNext(size_t i)
495 {
496 if (i >= cur_.size())
497 return;
498 next_.insert(next_.end(), std::next(cur_.begin(), i), cur_.end());
499 }
500
501 auto
502 size() const
503 {
504 return cur_.size();
505 }
506
507 void
508 removeIndex(std::size_t i)
509 {
510 if (i >= next_.size())
511 return;
512 next_.erase(next_.begin() + i);
513 }
514};
516
537template <class TInAmt, class TOutAmt>
538FlowResult<TInAmt, TOutAmt>
540 PaymentSandbox const& baseView,
541 std::vector<Strand> const& strands,
542 TOutAmt const& outReq,
543 bool partialPayment,
544 OfferCrossing offerCrossing,
545 std::optional<Quality> const& limitQuality,
546 std::optional<STAmount> const& sendMaxST,
548 AMMContext& ammContext,
549 path::detail::FlowDebugInfo* flowDebugInfo = nullptr)
550{
551 // Used to track the strand that offers the best quality (output/input
552 // ratio)
553 struct BestStrand
554 {
555 TInAmt in;
556 TOutAmt out;
558 Strand const& strand;
559 Quality quality;
560
561 BestStrand(
562 TInAmt const& in_,
563 TOutAmt const& out_,
564 PaymentSandbox&& sb_,
565 Strand const& strand_,
566 Quality const& quality_)
567 : in(in_), out(out_), sb(std::move(sb_)), strand(strand_), quality(quality_)
568 {
569 }
570 };
571
572 std::size_t const maxTries = 1000;
573 std::size_t curTry = 0;
574 std::uint32_t const maxOffersToConsider = 1500;
575 std::uint32_t offersConsidered = 0;
576
577 // There is a bug in gcc that incorrectly warns about using uninitialized
578 // values if `remainingIn` is initialized through a copy constructor. We can
579 // get similar warnings for `sendMax` if it is initialized in the most
580 // natural way. Using `make_optional`, allows us to work around this bug.
581 TInAmt const sendMaxInit = sendMaxST ? toAmount<TInAmt>(*sendMaxST) : TInAmt{beast::zero};
582 std::optional<TInAmt> const sendMax =
583 (sendMaxST && sendMaxInit >= beast::zero) ? std::make_optional(sendMaxInit) : std::nullopt;
584 std::optional<TInAmt> remainingIn = !!sendMax ? std::make_optional(sendMaxInit) : std::nullopt;
585 // std::optional<TInAmt> remainingIn{sendMax};
586
587 TOutAmt remainingOut(outReq);
588
589 PaymentSandbox sb(&baseView);
590
591 // non-dry strands
592 ActiveStrands activeStrands(strands);
593
594 // Keeping a running sum of the amount in the order they are processed
595 // will not give the best precision. Keep a collection so they may be summed
596 // from smallest to largest
597 boost::container::flat_multiset<TInAmt> savedIns;
598 savedIns.reserve(maxTries);
599 boost::container::flat_multiset<TOutAmt> savedOuts;
600 savedOuts.reserve(maxTries);
601
602 auto sum = [](auto const& col) {
603 using TResult = std::decay_t<decltype(*col.begin())>;
604 if (col.empty())
605 return TResult{beast::zero};
606 return std::accumulate(col.begin() + 1, col.end(), *col.begin());
607 };
608
609 // These offers only need to be removed if the payment is not
610 // successful
611 boost::container::flat_set<uint256> ofrsToRmOnFail;
612
613 while (remainingOut > beast::zero && (!remainingIn || *remainingIn > beast::zero))
614 {
615 ++curTry;
616 if (curTry >= maxTries)
617 {
618 return {telFAILED_PROCESSING, std::move(ofrsToRmOnFail)};
619 }
620
621 activeStrands.activateNext(sb, limitQuality);
622
623 ammContext.setMultiPath(activeStrands.size() > 1);
624
625 // Limit only if one strand and limitQuality
626 auto const limitRemainingOut = [&]() {
627 if (activeStrands.size() == 1 && limitQuality)
628 if (auto const strand = activeStrands.get(0))
629 return limitOut(sb, *strand, remainingOut, *limitQuality);
630 return remainingOut;
631 }();
632 auto const adjustedRemOut = limitRemainingOut != remainingOut;
633
634 boost::container::flat_set<uint256> ofrsToRm;
636 if (flowDebugInfo)
637 flowDebugInfo->newLiquidityPass();
638 // Index of strand to mark as inactive (remove from the active list) if
639 // the liquidity is used. This is used for strands that consume too many
640 // offers Constructed as `false,0` to workaround a gcc warning about
641 // uninitialized variables
642 std::optional<std::size_t> markInactiveOnUse;
643 for (size_t strandIndex = 0, sie = activeStrands.size(); strandIndex != sie; ++strandIndex)
644 {
645 Strand const* strand = activeStrands.get(strandIndex);
646 if (!strand)
647 {
648 // should not happen
649 continue;
650 }
651 // Clear AMM liquidity used flag. The flag might still be set if
652 // the previous strand execution failed. It has to be reset
653 // since this strand might not have AMM liquidity.
654 ammContext.clear();
655 if (offerCrossing && limitQuality)
656 {
657 auto const strandQ = qualityUpperBound(sb, *strand);
658 if (!strandQ || *strandQ < *limitQuality)
659 continue;
660 }
661 auto f = flow<TInAmt, TOutAmt>(sb, *strand, remainingIn, limitRemainingOut, j);
662
663 // rm bad offers even if the strand fails
664 SetUnion(ofrsToRm, f.ofrsToRm);
665
666 offersConsidered += f.ofrsUsed;
667
668 if (!f.success || f.out == beast::zero)
669 continue;
670
671 if (flowDebugInfo)
672 flowDebugInfo->pushLiquiditySrc(EitherAmount(f.in), EitherAmount(f.out));
673
674 XRPL_ASSERT(
675 f.out <= remainingOut && f.sandbox && (!remainingIn || f.in <= *remainingIn),
676 "xrpl::flow : remaining constraints");
677
678 Quality const q(f.out, f.in);
679
680 JLOG(j.trace()) << "New flow iter (iter, in, out): " << curTry - 1 << " "
681 << to_string(f.in) << " " << to_string(f.out);
682
683 // limitOut() finds output to generate exact requested
684 // limitQuality. But the actual limit quality might be slightly
685 // off due to the round off.
686 if (limitQuality && q < *limitQuality &&
687 (!adjustedRemOut || !withinRelativeDistance(q, *limitQuality, Number(1, -7))))
688 {
689 JLOG(j.trace()) << "Path rejected by limitQuality"
690 << " limit: " << *limitQuality << " path q: " << q;
691 continue;
692 }
693
694 XRPL_ASSERT(!best, "xrpl::flow : best is unset");
695 if (!f.inactive)
696 activeStrands.push(strand);
697 best.emplace(f.in, f.out, std::move(*f.sandbox), *strand, q);
698 activeStrands.pushRemainingCurToNext(strandIndex + 1);
699 break;
700 }
701
702 bool const shouldBreak = !best || offersConsidered >= maxOffersToConsider;
703
704 if (best)
705 {
706 if (markInactiveOnUse)
707 {
708 activeStrands.removeIndex(*markInactiveOnUse);
709 markInactiveOnUse.reset();
710 }
711 savedIns.insert(best->in);
712 savedOuts.insert(best->out);
713 remainingOut = outReq - sum(savedOuts);
714 if (sendMax)
715 remainingIn = *sendMax - sum(savedIns);
716
717 if (flowDebugInfo)
718 flowDebugInfo->pushPass(
719 EitherAmount(best->in), EitherAmount(best->out), activeStrands.size());
720
721 JLOG(j.trace()) << "Best path: in: " << to_string(best->in)
722 << " out: " << to_string(best->out)
723 << " remainingOut: " << to_string(remainingOut);
724
725 best->sb.apply(sb);
726 ammContext.update();
727 }
728 else
729 {
730 JLOG(j.trace()) << "All strands dry.";
731 }
732
733 best.reset(); // view in best must be destroyed before modifying base
734 // view
735 if (!ofrsToRm.empty())
736 {
737 SetUnion(ofrsToRmOnFail, ofrsToRm);
738 for (auto const& o : ofrsToRm)
739 {
740 if (auto ok = sb.peek(keylet::offer(o)))
741 offerDelete(sb, ok, j);
742 }
743 }
744
745 if (shouldBreak)
746 break;
747 }
748
749 auto const actualOut = sum(savedOuts);
750 auto const actualIn = sum(savedIns);
751
752 JLOG(j.trace()) << "Total flow: in: " << to_string(actualIn)
753 << " out: " << to_string(actualOut);
754
755 /* flowCross doesn't handle offer crossing with tfFillOrKill flag correctly.
756 * 1. If tfFillOrKill is set then the owner must receive the full
757 * TakerPays. We reverse pays and gets because during crossing
758 * we are taking, therefore the owner must deliver the full TakerPays and
759 * the entire TakerGets doesn't have to be spent.
760 * Pre-fixFillOrKill amendment code fails if the entire TakerGets
761 * is not spent. fixFillOrKill addresses this issue.
762 * 2. If tfSell is also set then the owner must spend the entire TakerGets
763 * even if it means obtaining more than TakerPays. Since the pays and gets
764 * are reversed, the owner must send the entire TakerGets.
765 */
766 bool const fillOrKillEnabled = baseView.rules().enabled(fixFillOrKill);
767
768 if (actualOut != outReq)
769 {
770 if (actualOut > outReq)
771 {
772 // Rounding in the payment engine is causing this assert to
773 // sometimes fire with "dust" amounts. This is causing issues when
774 // running debug builds of rippled. While this issue still needs to
775 // be resolved, the assert is causing more harm than good at this
776 // point.
777 // UNREACHABLE("xrpl::flow : rounding error");
778
779 return {tefEXCEPTION, std::move(ofrsToRmOnFail)};
780 }
781 if (!partialPayment)
782 {
783 // If we're offerCrossing a !partialPayment, then we're
784 // handling tfFillOrKill.
785 // Pre-fixFillOrKill amendment:
786 // That case is handled below; not here.
787 // fixFillOrKill amendment:
788 // That case is handled here if tfSell is also not set; i.e,
789 // case 1.
790 if (!offerCrossing || (fillOrKillEnabled && offerCrossing != OfferCrossing::sell))
791 return {tecPATH_PARTIAL, actualIn, actualOut, std::move(ofrsToRmOnFail)};
792 }
793 else if (actualOut == beast::zero)
794 {
795 return {tecPATH_DRY, std::move(ofrsToRmOnFail)};
796 }
797 }
798 if (offerCrossing &&
799 (!partialPayment && (!fillOrKillEnabled || offerCrossing == OfferCrossing::sell)))
800 {
801 // If we're offer crossing and partialPayment is *not* true, then
802 // we're handling a FillOrKill offer. In this case remainingIn must
803 // be zero (all funds must be consumed) or else we kill the offer.
804 // Pre-fixFillOrKill amendment:
805 // Handles both cases 1. and 2.
806 // fixFillOrKill amendment:
807 // Handles 2. 1. is handled above and falls through for tfSell.
808 XRPL_ASSERT(remainingIn, "xrpl::flow : nonzero remainingIn");
809 if (remainingIn && *remainingIn != beast::zero)
810 return {tecPATH_PARTIAL, actualIn, actualOut, std::move(ofrsToRmOnFail)};
811 }
812
813 return {actualIn, actualOut, std::move(sb), std::move(ofrsToRmOnFail)};
814}
815
816} // namespace xrpl
T accumulate(T... args)
T any_of(T... args)
T begin(T... args)
A generic endpoint for log messages.
Definition Journal.h:40
Stream fatal() const
Definition Journal.h:325
Stream trace() const
Severity stream access functions.
Definition Journal.h:295
Stream warn() const
Definition Journal.h:313
Maintains AMM info per overall payment engine execution and individual iteration.
Definition AMMContext.h:16
void setMultiPath(bool fs)
Definition AMMContext.h:49
void clear()
Strand execution may fail.
Definition AMMContext.h:90
Number is a floating point type that can represent a wide range of values.
Definition Number.h:207
A wrapper which makes credits unavailable to balances.
bool enabled(uint256 const &feature) const
Returns true if a feature is enabled.
Definition Rules.cpp:120
static std::uint64_t const uRateOne
Definition STAmount.h:62
std::shared_ptr< SLE > peek(Keylet const &k) override
Prepare to modify the SLE associated with key.
Rules const & rules() const override
Returns the tx processing rules.
T clear(T... args)
T emplace(T... args)
T empty(T... args)
T end(T... args)
T erase(T... args)
T insert(T... args)
T is_same_v
T make_optional(T... args)
T min(T... args)
T move(T... args)
STL namespace.
TER valid(STTx const &tx, ReadView const &view, AccountID const &src, beast::Journal j)
Keylet offer(AccountID const &id, std::uint32_t seq) noexcept
An offer from an account.
Definition Indexes.cpp:243
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
@ telFAILED_PROCESSING
Definition TER.h:36
static auto sum(TCollection const &col)
Definition BookStep.cpp:927
Quality composed_quality(Quality const &lhs, Quality const &rhs)
Definition Quality.cpp:113
static void limitStepOut(Offer const &offer, TAmounts< TIn, TOut > &ofrAmt, TAmounts< TIn, TOut > &stpAmt, TOut &ownerGives, std::uint32_t transferRateIn, std::uint32_t transferRateOut, TOut const &limit)
Definition BookStep.cpp:627
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:602
T get(Section const &section, std::string const &name, T const &defaultValue=T{})
Retrieve a key/value pair from a section.
@ tefEXCEPTION
Definition TER.h:152
DebtDirection
Definition Steps.h:21
void SetUnion(boost::container::flat_set< T > &dst, boost::container::flat_set< T > const &src)
Given two flat sets dst and src, compute dst = dst union src.
Definition FlatSets.h:15
StrandResult< TInAmt, TOutAmt > flow(PaymentSandbox const &baseView, Strand const &strand, std::optional< TInAmt > const &maxIn, TOutAmt const &out, beast::Journal j)
Request out amount from a strand.
Definition StrandFlow.h:82
TER offerDelete(ApplyView &view, std::shared_ptr< SLE > const &sle, beast::Journal j)
Delete an offer.
boost::outcome_v2::result< T, std::error_code > Result
Definition b58_utils.h:17
bool withinRelativeDistance(Quality const &calcQuality, Quality const &reqQuality, Number const &dist)
Check if the relative distance between the qualities is within the requested distance.
Definition AMMHelpers.h:106
@ tecPATH_PARTIAL
Definition TER.h:263
@ tecPATH_DRY
Definition TER.h:275
OfferCrossing
Definition Steps.h:24
@ sell
Definition Steps.h:24
T next(T... args)
T push_back(T... args)
T reserve(T... args)
T reset(T... args)
T size(T... args)
T stable_sort(T... args)
Result of flow() execution of a single Strand.
Definition StrandFlow.h:29
TOutAmt out
Currency amount out.
Definition StrandFlow.h:32
boost::container::flat_set< uint256 > ofrsToRm
Offers to remove.
Definition StrandFlow.h:34
std::optional< PaymentSandbox > sandbox
Resulting Sandbox state.
Definition StrandFlow.h:33
bool success
Strand succeeded.
Definition StrandFlow.h:30
std::uint32_t ofrsUsed
Definition StrandFlow.h:37
StrandResult()=default
Strand result constructor.
StrandResult(Strand const &strand, TInAmt const &in_, TOutAmt const &out_, PaymentSandbox &&sandbox_, boost::container::flat_set< uint256 > ofrsToRm_, bool inactive_)
Definition StrandFlow.h:46
StrandResult(Strand const &strand, boost::container::flat_set< uint256 > ofrsToRm_)
Definition StrandFlow.h:63
TInAmt in
Currency amount in.
Definition StrandFlow.h:31
bool inactive
Strand should not considered as a further source of liquidity (dry)
Definition StrandFlow.h:40
T swap(T... args)
T tie(T... args)