rippled
Loading...
Searching...
No Matches
Taker.cpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2014 Ripple Labs Inc.
5
6 Permission to use, copy, modify, and/or distribute this software for any
7 purpose with or without fee is hereby granted, provided that the above
8 copyright notice and this permission notice appear in all copies.
9
10 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17*/
18//==============================================================================
19
20#include <xrpld/app/tx/detail/Taker.h>
21#include <xrpl/basics/Log.h>
22#include <xrpl/basics/contract.h>
23
24namespace ripple {
25
26static std::string
27format_amount(STAmount const& amount)
28{
29 std::string txt = amount.getText();
30 txt += "/";
31 txt += to_string(amount.issue().currency);
32 return txt;
33}
34
36 CrossType cross_type,
37 AccountID const& account,
38 Amounts const& amount,
39 Quality const& quality,
40 std::uint32_t flags,
41 Rate const& rate_in,
42 Rate const& rate_out,
43 beast::Journal journal)
44 : account_(account)
45 , quality_(quality)
46 , threshold_(quality_)
47 , sell_(flags & tfSell)
48 , original_(amount)
49 , remaining_(amount)
50 , issue_in_(remaining_.in.issue())
51 , issue_out_(remaining_.out.issue())
52 , m_rate_in(rate_in)
53 , m_rate_out(rate_out)
54 , cross_type_(cross_type)
55 , journal_(journal)
56{
57 XRPL_ASSERT(
58 remaining_.in > beast::zero,
59 "ripple::BasicTaker::BasicTaker : positive remaining in");
60 XRPL_ASSERT(
61 remaining_.out > beast::zero,
62 "ripple::BasicTaker::BasicTaker : positive remaining out");
63
64 XRPL_ASSERT(
65 m_rate_in.value, "ripple::BasicTaker::BasicTaker : nonzero rate in");
66 XRPL_ASSERT(
67 m_rate_out.value, "ripple::BasicTaker::BasicTaker : nonzero rate out");
68
69 // If we are dealing with a particular flavor, make sure that it's the
70 // flavor we expect:
71 XRPL_ASSERT(
73 (isXRP(issue_in()) && !isXRP(issue_out())),
74 "ripple::BasicTaker::BasicTaker : valid cross to IOU");
75
76 XRPL_ASSERT(
78 (!isXRP(issue_in()) && isXRP(issue_out())),
79 "ripple::BasicTaker::BasicTaker : valid cross to XRP");
80
81 // And make sure we're not crossing XRP for XRP
82 XRPL_ASSERT(
83 !isXRP(issue_in()) || !isXRP(issue_out()),
84 "ripple::BasicTaker::BasicTaker : not crossing XRP for XRP");
85
86 // If this is a passive order, we adjust the quality so as to prevent offers
87 // at the same quality level from being consumed.
88 if (flags & tfPassive)
89 ++threshold_;
90}
91
92Rate
94 Rate const& rate,
95 Issue const& issue,
96 AccountID const& from,
97 AccountID const& to)
98{
99 // If there's a transfer rate, the issuer is not involved
100 // and the sender isn't the same as the recipient, return
101 // the actual transfer rate.
102 if (rate != parityRate && from != to && from != issue.account &&
103 to != issue.account)
104 {
105 return rate;
106 }
107
108 return parityRate;
109}
110
111bool
113{
114 if (get_funds(account(), remaining_.in) > beast::zero)
115 return false;
116
117 JLOG(journal_.debug()) << "Unfunded: taker is out of funds.";
118 return true;
119}
120
121bool
123{
124 // We are done if we have consumed all the input currency
125 if (remaining_.in <= beast::zero)
126 {
127 JLOG(journal_.debug())
128 << "Done: all the input currency has been consumed.";
129 return true;
130 }
131
132 // We are done if using buy semantics and we received the
133 // desired amount of output currency
134 if (!sell_ && (remaining_.out <= beast::zero))
135 {
136 JLOG(journal_.debug()) << "Done: the desired amount has been received.";
137 return true;
138 }
139
140 // We are done if the taker is out of funds
141 if (unfunded())
142 {
143 JLOG(journal_.debug()) << "Done: taker out of funds.";
144 return true;
145 }
146
147 return false;
148}
149
150Amounts
152{
153 // If the taker is done, then there's no offer to place.
154 if (done())
155 return Amounts(remaining_.in.zeroed(), remaining_.out.zeroed());
156
157 // Avoid math altogether if we didn't cross.
158 if (original_ == remaining_)
159 return original_;
160
161 if (sell_)
162 {
163 XRPL_ASSERT(
164 remaining_.in > beast::zero,
165 "ripple::BasicTaker::remaining_offer : positive remaining in");
166
167 // We scale the output based on the remaining input:
168 return Amounts(
169 remaining_.in,
170 divRound(remaining_.in, quality_.rate(), issue_out_, true));
171 }
172
173 XRPL_ASSERT(
174 remaining_.out > beast::zero,
175 "ripple::BasicTaker::remaining_offer : positive remaining out");
176
177 // We scale the input based on the remaining output:
178 return Amounts(
179 mulRound(remaining_.out, quality_.rate(), issue_in_, true),
180 remaining_.out);
181}
182
183Amounts const&
185{
186 return original_;
187}
188
189// TODO: the presence of 'output' is an artifact caused by the fact that
190// Amounts carry issue information which should be decoupled.
191static STAmount
192qual_div(STAmount const& amount, Quality const& quality, STAmount const& output)
193{
194 auto result = divide(amount, quality.rate(), output.issue());
195 return std::min(result, output);
196}
197
198static STAmount
199qual_mul(STAmount const& amount, Quality const& quality, STAmount const& output)
200{
201 auto result = multiply(amount, quality.rate(), output.issue());
202 return std::min(result, output);
203}
204
205void
206BasicTaker::log_flow(char const* description, Flow const& flow)
207{
208 auto stream = journal_.debug();
209 if (!stream)
210 return;
211
212 stream << description;
213
214 if (isXRP(issue_in()))
215 stream << " order in: " << format_amount(flow.order.in);
216 else
217 stream << " order in: " << format_amount(flow.order.in)
218 << " (issuer: " << format_amount(flow.issuers.in) << ")";
219
220 if (isXRP(issue_out()))
221 stream << " order out: " << format_amount(flow.order.out);
222 else
223 stream << " order out: " << format_amount(flow.order.out)
224 << " (issuer: " << format_amount(flow.issuers.out) << ")";
225}
226
229 Amounts const& order,
230 Quality quality,
231 STAmount const& owner_funds,
232 STAmount const& taker_funds,
233 Rate const& rate_out)
234{
235 Flow f;
236 f.order = order;
237 f.issuers.out = multiply(f.order.out, rate_out);
238
239 log_flow("flow_xrp_to_iou", f);
240
241 // Clamp on owner balance
242 if (owner_funds < f.issuers.out)
243 {
244 f.issuers.out = owner_funds;
245 f.order.out = divide(f.issuers.out, rate_out);
246 f.order.in = qual_mul(f.order.out, quality, f.order.in);
247 log_flow("(clamped on owner balance)", f);
248 }
249
250 // Clamp if taker wants to limit the output
251 if (!sell_ && remaining_.out < f.order.out)
252 {
253 f.order.out = remaining_.out;
254 f.order.in = qual_mul(f.order.out, quality, f.order.in);
255 f.issuers.out = multiply(f.order.out, rate_out);
256 log_flow("(clamped on taker output)", f);
257 }
258
259 // Clamp on the taker's funds
260 if (taker_funds < f.order.in)
261 {
262 f.order.in = taker_funds;
263 f.order.out = qual_div(f.order.in, quality, f.order.out);
264 f.issuers.out = multiply(f.order.out, rate_out);
265 log_flow("(clamped on taker funds)", f);
266 }
267
268 // Clamp on remaining offer if we are not handling the second leg
269 // of an autobridge.
270 if (cross_type_ == CrossType::XrpToIou && (remaining_.in < f.order.in))
271 {
272 f.order.in = remaining_.in;
273 f.order.out = qual_div(f.order.in, quality, f.order.out);
274 f.issuers.out = multiply(f.order.out, rate_out);
275 log_flow("(clamped on taker input)", f);
276 }
277
278 return f;
279}
280
283 Amounts const& order,
284 Quality quality,
285 STAmount const& owner_funds,
286 STAmount const& taker_funds,
287 Rate const& rate_in)
288{
289 Flow f;
290 f.order = order;
291 f.issuers.in = multiply(f.order.in, rate_in);
292
293 log_flow("flow_iou_to_xrp", f);
294
295 // Clamp on owner's funds
296 if (owner_funds < f.order.out)
297 {
298 f.order.out = owner_funds;
299 f.order.in = qual_mul(f.order.out, quality, f.order.in);
300 f.issuers.in = multiply(f.order.in, rate_in);
301 log_flow("(clamped on owner funds)", f);
302 }
303
304 // Clamp if taker wants to limit the output and we are not the
305 // first leg of an autobridge.
307 {
308 if (remaining_.out < f.order.out)
309 {
310 f.order.out = remaining_.out;
311 f.order.in = qual_mul(f.order.out, quality, f.order.in);
312 f.issuers.in = multiply(f.order.in, rate_in);
313 log_flow("(clamped on taker output)", f);
314 }
315 }
316
317 // Clamp on the taker's input offer
318 if (remaining_.in < f.order.in)
319 {
320 f.order.in = remaining_.in;
321 f.issuers.in = multiply(f.order.in, rate_in);
322 f.order.out = qual_div(f.order.in, quality, f.order.out);
323 log_flow("(clamped on taker input)", f);
324 }
325
326 // Clamp on the taker's input balance
327 if (taker_funds < f.issuers.in)
328 {
329 f.issuers.in = taker_funds;
330 f.order.in = divide(f.issuers.in, rate_in);
331 f.order.out = qual_div(f.order.in, quality, f.order.out);
332 log_flow("(clamped on taker funds)", f);
333 }
334
335 return f;
336}
337
340 Amounts const& order,
341 Quality quality,
342 STAmount const& owner_funds,
343 STAmount const& taker_funds,
344 Rate const& rate_in,
345 Rate const& rate_out)
346{
347 Flow f;
348 f.order = order;
349 f.issuers.in = multiply(f.order.in, rate_in);
350 f.issuers.out = multiply(f.order.out, rate_out);
351
352 log_flow("flow_iou_to_iou", f);
353
354 // Clamp on owner balance
355 if (owner_funds < f.issuers.out)
356 {
357 f.issuers.out = owner_funds;
358 f.order.out = divide(f.issuers.out, rate_out);
359 f.order.in = qual_mul(f.order.out, quality, f.order.in);
360 f.issuers.in = multiply(f.order.in, rate_in);
361 log_flow("(clamped on owner funds)", f);
362 }
363
364 // Clamp on taker's offer
365 if (!sell_ && remaining_.out < f.order.out)
366 {
367 f.order.out = remaining_.out;
368 f.order.in = qual_mul(f.order.out, quality, f.order.in);
369 f.issuers.out = multiply(f.order.out, rate_out);
370 f.issuers.in = multiply(f.order.in, rate_in);
371 log_flow("(clamped on taker output)", f);
372 }
373
374 // Clamp on the taker's input offer
375 if (remaining_.in < f.order.in)
376 {
377 f.order.in = remaining_.in;
378 f.issuers.in = multiply(f.order.in, rate_in);
379 f.order.out = qual_div(f.order.in, quality, f.order.out);
380 f.issuers.out = multiply(f.order.out, rate_out);
381 log_flow("(clamped on taker input)", f);
382 }
383
384 // Clamp on the taker's input balance
385 if (taker_funds < f.issuers.in)
386 {
387 f.issuers.in = taker_funds;
388 f.order.in = divide(f.issuers.in, rate_in);
389 f.order.out = qual_div(f.order.in, quality, f.order.out);
390 f.issuers.out = multiply(f.order.out, rate_out);
391 log_flow("(clamped on taker funds)", f);
392 }
393
394 return f;
395}
396
397// Calculates the direct flow through the specified offer
399BasicTaker::do_cross(Amounts offer, Quality quality, AccountID const& owner)
400{
401 auto const owner_funds = get_funds(owner, offer.out);
402 auto const taker_funds = get_funds(account(), offer.in);
403
404 Flow result;
405
407 {
408 result = flow_xrp_to_iou(
409 offer,
410 quality,
411 owner_funds,
412 taker_funds,
413 out_rate(owner, account()));
414 }
416 {
417 result = flow_iou_to_xrp(
418 offer,
419 quality,
420 owner_funds,
421 taker_funds,
422 in_rate(owner, account()));
423 }
424 else
425 {
426 result = flow_iou_to_iou(
427 offer,
428 quality,
429 owner_funds,
430 taker_funds,
431 in_rate(owner, account()),
432 out_rate(owner, account()));
433 }
434
435 if (!result.sanity_check())
436 Throw<std::logic_error>("Computed flow fails sanity check.");
437
438 remaining_.out -= result.order.out;
439 remaining_.in -= result.order.in;
440
441 XRPL_ASSERT(
442 remaining_.in >= beast::zero,
443 "ripple::BasicTaker::do_cross : minimum remaining in");
444
445 return result;
446}
447
448// Calculates the bridged flow through the specified offers
451 Amounts offer1,
452 Quality quality1,
453 AccountID const& owner1,
454 Amounts offer2,
455 Quality quality2,
456 AccountID const& owner2)
457{
458 XRPL_ASSERT(
459 !offer1.in.native(),
460 "ripple::BasicTaker::do_cross : offer1 in is not XRP");
461 XRPL_ASSERT(
462 offer1.out.native(),
463 "ripple::BasicTaker::do_cross : offer1 out is XRP");
464 XRPL_ASSERT(
465 offer2.in.native(), "ripple::BasicTaker::do_cross : offer2 in is XRP");
466 XRPL_ASSERT(
467 !offer2.out.native(),
468 "ripple::BasicTaker::do_cross : offer2 out is not XRP");
469
470 // If the taker owns the first leg of the offer, then the taker's available
471 // funds aren't the limiting factor for the input - the offer itself is.
472 auto leg1_in_funds = get_funds(account(), offer1.in);
473
474 if (account() == owner1)
475 {
476 JLOG(journal_.trace()) << "The taker owns the first leg of a bridge.";
477 leg1_in_funds = std::max(leg1_in_funds, offer1.in);
478 }
479
480 // If the taker owns the second leg of the offer, then the taker's available
481 // funds are not the limiting factor for the output - the offer itself is.
482 auto leg2_out_funds = get_funds(owner2, offer2.out);
483
484 if (account() == owner2)
485 {
486 JLOG(journal_.trace()) << "The taker owns the second leg of a bridge.";
487 leg2_out_funds = std::max(leg2_out_funds, offer2.out);
488 }
489
490 // The amount available to flow via XRP is the amount that the owner of the
491 // first leg of the bridge has, up to the first leg's output.
492 //
493 // But, when both legs of a bridge are owned by the same person, the amount
494 // of XRP that can flow between the two legs is, essentially, infinite
495 // since all the owner is doing is taking out XRP of his left pocket
496 // and putting it in his right pocket. In that case, we set the available
497 // XRP to the largest of the two offers.
498 auto xrp_funds = get_funds(owner1, offer1.out);
499
500 if (owner1 == owner2)
501 {
502 JLOG(journal_.trace())
503 << "The bridge endpoints are owned by the same account.";
504 xrp_funds = std::max(offer1.out, offer2.in);
505 }
506
507 if (auto stream = journal_.debug())
508 {
509 stream << "Available bridge funds:";
510 stream << " leg1 in: " << format_amount(leg1_in_funds);
511 stream << " leg2 out: " << format_amount(leg2_out_funds);
512 stream << " xrp: " << format_amount(xrp_funds);
513 }
514
515 auto const leg1_rate = in_rate(owner1, account());
516 auto const leg2_rate = out_rate(owner2, account());
517
518 // Attempt to determine the maximal flow that can be achieved across each
519 // leg independent of the other.
520 auto flow1 =
521 flow_iou_to_xrp(offer1, quality1, xrp_funds, leg1_in_funds, leg1_rate);
522
523 if (!flow1.sanity_check())
524 Throw<std::logic_error>("Computed flow1 fails sanity check.");
525
526 auto flow2 =
527 flow_xrp_to_iou(offer2, quality2, leg2_out_funds, xrp_funds, leg2_rate);
528
529 if (!flow2.sanity_check())
530 Throw<std::logic_error>("Computed flow2 fails sanity check.");
531
532 // We now have the maximal flows across each leg individually. We need to
533 // equalize them, so that the amount of XRP that flows out of the first leg
534 // is the same as the amount of XRP that flows into the second leg. We take
535 // the side which is the limiting factor (if any) and adjust the other.
536 if (flow1.order.out < flow2.order.in)
537 {
538 // Adjust the second leg of the offer down:
539 flow2.order.in = flow1.order.out;
540 flow2.order.out = qual_div(flow2.order.in, quality2, flow2.order.out);
541 flow2.issuers.out = multiply(flow2.order.out, leg2_rate);
542 log_flow("Balancing: adjusted second leg down", flow2);
543 }
544 else if (flow1.order.out > flow2.order.in)
545 {
546 // Adjust the first leg of the offer down:
547 flow1.order.out = flow2.order.in;
548 flow1.order.in = qual_mul(flow1.order.out, quality1, flow1.order.in);
549 flow1.issuers.in = multiply(flow1.order.in, leg1_rate);
550 log_flow("Balancing: adjusted first leg down", flow2);
551 }
552
553 if (flow1.order.out != flow2.order.in)
554 Throw<std::logic_error>("Bridged flow is out of balance.");
555
556 remaining_.out -= flow2.order.out;
557 remaining_.in -= flow1.order.in;
558
559 return std::make_pair(flow1, flow2);
560}
561
562//==============================================================================
563
565 CrossType cross_type,
566 ApplyView& view,
567 AccountID const& account,
568 Amounts const& offer,
569 std::uint32_t flags,
570 beast::Journal journal)
571 : BasicTaker(
572 cross_type,
573 account,
574 offer,
575 Quality(offer),
576 flags,
577 calculateRate(view, offer.in.getIssuer(), account),
578 calculateRate(view, offer.out.getIssuer(), account),
579 journal)
580 , view_(view)
581 , xrp_flow_(0)
582 , direct_crossings_(0)
583 , bridge_crossings_(0)
584{
585 XRPL_ASSERT(
586 issue_in() == offer.in.issue(),
587 "ripple::Taker::Taker : issue in is a match");
588 XRPL_ASSERT(
589 issue_out() == offer.out.issue(),
590 "ripple::Taker::Taker : issue out is a match");
591
592 if (auto stream = journal_.debug())
593 {
594 stream << "Crossing as: " << to_string(account);
595
596 if (isXRP(issue_in()))
597 stream << " Offer in: " << format_amount(offer.in);
598 else
599 stream << " Offer in: " << format_amount(offer.in)
600 << " (issuer: " << issue_in().account << ")";
601
602 if (isXRP(issue_out()))
603 stream << " Offer out: " << format_amount(offer.out);
604 else
605 stream << " Offer out: " << format_amount(offer.out)
606 << " (issuer: " << issue_out().account << ")";
607
608 stream << " Balance: "
609 << format_amount(get_funds(account, offer.in));
610 }
611}
612
613void
614Taker::consume_offer(Offer& offer, Amounts const& order)
615{
616 if (order.in < beast::zero)
617 Throw<std::logic_error>("flow with negative input.");
618
619 if (order.out < beast::zero)
620 Throw<std::logic_error>("flow with negative output.");
621
622 JLOG(journal_.debug()) << "Consuming from offer " << offer;
623
624 if (auto stream = journal_.trace())
625 {
626 auto const& available = offer.amount();
627
628 stream << " in:" << format_amount(available.in);
629 stream << " out:" << format_amount(available.out);
630 }
631
632 offer.consume(view_, order);
633}
634
636Taker::get_funds(AccountID const& account, STAmount const& amount) const
637{
639}
640
641TER
643 AccountID const& from,
644 AccountID const& to,
645 STAmount const& amount)
646{
647 if (!isXRP(amount))
648 Throw<std::logic_error>("Using transferXRP with IOU");
649
650 if (from == to)
651 return tesSUCCESS;
652
653 // Transferring zero is equivalent to not doing a transfer
654 if (amount == beast::zero)
655 return tesSUCCESS;
656
657 return ripple::transferXRP(view_, from, to, amount, journal_);
658}
659
660TER
662 AccountID const& account,
663 STAmount const& amount,
664 Issue const& issue)
665{
666 if (isXRP(amount))
667 Throw<std::logic_error>("Using redeemIOU with XRP");
668
669 if (account == issue.account)
670 return tesSUCCESS;
671
672 // Transferring zero is equivalent to not doing a transfer
673 if (amount == beast::zero)
674 return tesSUCCESS;
675
676 // If we are trying to redeem some amount, then the account
677 // must have a credit balance.
678 if (get_funds(account, amount) <= beast::zero)
679 Throw<std::logic_error>("redeemIOU has no funds to redeem");
680
681 auto ret = ripple::redeemIOU(view_, account, amount, issue, journal_);
682
683 if (get_funds(account, amount) < beast::zero)
684 Throw<std::logic_error>("redeemIOU redeemed more funds than available");
685
686 return ret;
687}
688
689TER
691 AccountID const& account,
692 STAmount const& amount,
693 Issue const& issue)
694{
695 if (isXRP(amount))
696 Throw<std::logic_error>("Using issueIOU with XRP");
697
698 if (account == issue.account)
699 return tesSUCCESS;
700
701 // Transferring zero is equivalent to not doing a transfer
702 if (amount == beast::zero)
703 return tesSUCCESS;
704
705 return ripple::issueIOU(view_, account, amount, issue, journal_);
706}
707
708// Performs funds transfers to fill the given offer and adjusts offer.
709TER
711{
712 // adjust offer
713 consume_offer(offer, flow.order);
714
715 TER result = tesSUCCESS;
716
718 {
719 XRPL_ASSERT(
720 !isXRP(flow.order.in), "ripple::Taker::fill : order in is not XRP");
721
722 if (result == tesSUCCESS)
723 result =
724 redeemIOU(account(), flow.issuers.in, flow.issuers.in.issue());
725
726 if (result == tesSUCCESS)
727 result =
728 issueIOU(offer.owner(), flow.order.in, flow.order.in.issue());
729 }
730 else
731 {
732 XRPL_ASSERT(
733 isXRP(flow.order.in), "ripple::Taker::fill : order in is XRP");
734
735 if (result == tesSUCCESS)
736 result = transferXRP(account(), offer.owner(), flow.order.in);
737 }
738
739 // Now send funds from the account whose offer we're taking
741 {
742 XRPL_ASSERT(
743 !isXRP(flow.order.out),
744 "ripple::Taker::fill : order out is not XRP");
745
746 if (result == tesSUCCESS)
747 result = redeemIOU(
748 offer.owner(), flow.issuers.out, flow.issuers.out.issue());
749
750 if (result == tesSUCCESS)
751 result =
752 issueIOU(account(), flow.order.out, flow.order.out.issue());
753 }
754 else
755 {
756 XRPL_ASSERT(
757 isXRP(flow.order.out), "ripple::Taker::fill : order out is XRP");
758
759 if (result == tesSUCCESS)
760 result = transferXRP(offer.owner(), account(), flow.order.out);
761 }
762
763 if (result == tesSUCCESS)
765
766 return result;
767}
768
769// Performs bridged funds transfers to fill the given offers and adjusts offers.
770TER
772 BasicTaker::Flow const& flow1,
773 Offer& leg1,
774 BasicTaker::Flow const& flow2,
775 Offer& leg2)
776{
777 // Adjust offers accordingly
778 consume_offer(leg1, flow1.order);
779 consume_offer(leg2, flow2.order);
780
781 TER result = tesSUCCESS;
782
783 // Taker to leg1: IOU
784 if (leg1.owner() != account())
785 {
786 if (result == tesSUCCESS)
787 result = redeemIOU(
788 account(), flow1.issuers.in, flow1.issuers.in.issue());
789
790 if (result == tesSUCCESS)
791 result =
792 issueIOU(leg1.owner(), flow1.order.in, flow1.order.in.issue());
793 }
794
795 // leg1 to leg2: bridging over XRP
796 if (result == tesSUCCESS)
797 result = transferXRP(leg1.owner(), leg2.owner(), flow1.order.out);
798
799 // leg2 to Taker: IOU
800 if (leg2.owner() != account())
801 {
802 if (result == tesSUCCESS)
803 result = redeemIOU(
804 leg2.owner(), flow2.issuers.out, flow2.issuers.out.issue());
805
806 if (result == tesSUCCESS)
807 result =
808 issueIOU(account(), flow2.order.out, flow2.order.out.issue());
809 }
810
811 if (result == tesSUCCESS)
812 {
814 xrp_flow_ += flow1.order.out;
815 }
816
817 return result;
818}
819
820TER
822{
823 // In direct crossings, at least one leg must not be XRP.
824 if (isXRP(offer.amount().in) && isXRP(offer.amount().out))
825 return tefINTERNAL;
826
827 auto const amount =
828 do_cross(offer.amount(), offer.quality(), offer.owner());
829
830 return fill(amount, offer);
831}
832
833TER
835{
836 // In bridged crossings, XRP must can't be the input to the first leg
837 // or the output of the second leg.
838 if (isXRP(leg1.amount().in) || isXRP(leg2.amount().out))
839 return tefINTERNAL;
840
841 auto ret = do_cross(
842 leg1.amount(),
843 leg1.quality(),
844 leg1.owner(),
845 leg2.amount(),
846 leg2.quality(),
847 leg2.owner());
848
849 return fill(ret.first, leg1, ret.second, leg2);
850}
851
852Rate
854 ApplyView const& view,
855 AccountID const& issuer,
856 AccountID const& account)
857{
858 return isXRP(issuer) || (account == issuer) ? parityRate
859 : transferRate(view, issuer);
860}
861
862} // namespace ripple
A generic endpoint for log messages.
Definition: Journal.h:59
Stream debug() const
Definition: Journal.h:317
Stream trace() const
Severity stream access functions.
Definition: Journal.h:311
Writeable view to a ledger, for applying a transaction.
Definition: ApplyView.h:140
State for the active party during order book or payment operations.
Definition: Taker.h:40
Quality quality_
Definition: Taker.h:43
Flow flow_iou_to_iou(Amounts const &offer, Quality quality, STAmount const &owner_funds, STAmount const &taker_funds, Rate const &rate_in, Rate const &rate_out)
Definition: Taker.cpp:339
bool done() const
Returns true if order crossing should not continue.
Definition: Taker.cpp:122
Rate in_rate(AccountID const &from, AccountID const &to) const
Definition: Taker.h:129
Flow flow_iou_to_xrp(Amounts const &offer, Quality quality, STAmount const &owner_funds, STAmount const &taker_funds, Rate const &rate_in)
Definition: Taker.cpp:282
CrossType cross_type_
Definition: Taker.h:64
Flow flow_xrp_to_iou(Amounts const &offer, Quality quality, STAmount const &owner_funds, STAmount const &taker_funds, Rate const &rate_out)
Definition: Taker.cpp:228
beast::Journal const journal_
Definition: Taker.h:67
CrossType cross_type() const
Returns the type of crossing that is being performed.
Definition: Taker.h:186
AccountID const & account() const noexcept
Returns the account identifier of the taker.
Definition: Taker.h:172
void log_flow(char const *description, Flow const &flow)
Definition: Taker.cpp:206
Rate const m_rate_out
Definition: Taker.h:61
Issue const & issue_in() const
Returns the Issue associated with the input of the offer.
Definition: Taker.h:193
Amounts remaining_offer() const
Returns the amount remaining on the offer.
Definition: Taker.cpp:151
Rate out_rate(AccountID const &from, AccountID const &to) const
Definition: Taker.h:136
Amounts const & original_offer() const
Returns the amount that the offer was originally placed at.
Definition: Taker.cpp:184
Amounts const original_
Definition: Taker.h:49
Issue const & issue_out_
Definition: Taker.h:56
Quality threshold_
Definition: Taker.h:44
Issue const & issue_out() const
Returns the Issue associated with the output of the offer.
Definition: Taker.h:200
Issue const & issue_in_
Definition: Taker.h:55
virtual STAmount get_funds(AccountID const &account, STAmount const &funds) const =0
Amounts remaining_
Definition: Taker.h:52
static Rate effective_rate(Rate const &rate, Issue const &issue, AccountID const &from, AccountID const &to)
Definition: Taker.cpp:93
Rate const m_rate_in
Definition: Taker.h:60
BasicTaker::Flow do_cross(Amounts offer, Quality quality, AccountID const &owner)
Perform direct crossing through given offer.
Definition: Taker.cpp:399
bool unfunded() const
Returns true if the taker has run out of funds.
Definition: Taker.cpp:112
A currency issued by an account.
Definition: Issue.h:36
AccountID account
Definition: Issue.h:39
Currency currency
Definition: Issue.h:38
std::string getText() const override
Definition: STAmount.cpp:515
Issue const & issue() const
Definition: STAmount.h:487
Quality quality() const noexcept
Returns the quality of the offer.
Definition: Offer.h:76
AccountID const & owner() const
Returns the account id of the offer's owner.
Definition: Offer.h:83
TAmounts< TIn, TOut > const & amount() const
Returns the in and out amounts.
Definition: Offer.h:92
ApplyView & view_
Definition: Taker.h:328
STAmount get_funds(AccountID const &account, STAmount const &funds) const override
Definition: Taker.cpp:636
TER issueIOU(AccountID const &account, STAmount const &amount, Issue const &issue)
Definition: Taker.cpp:690
static Rate calculateRate(ApplyView const &view, AccountID const &issuer, AccountID const &account)
Definition: Taker.cpp:853
std::uint32_t direct_crossings_
Definition: Taker.h:334
TER redeemIOU(AccountID const &account, STAmount const &amount, Issue const &issue)
Definition: Taker.cpp:661
STAmount xrp_flow_
Definition: Taker.h:331
std::uint32_t bridge_crossings_
Definition: Taker.h:337
TER fill(BasicTaker::Flow const &flow, Offer &offer)
Definition: Taker.cpp:710
TER transferXRP(AccountID const &from, AccountID const &to, STAmount const &amount)
Definition: Taker.cpp:642
void consume_offer(Offer &offer, Amounts const &order)
Definition: Taker.cpp:614
TER cross(Offer &offer)
Perform a direct or bridged offer crossing as appropriate.
Definition: Taker.cpp:821
Taker()=delete
T make_pair(T... args)
T max(T... args)
T min(T... args)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: algorithm.h:26
STAmount divide(STAmount const &amount, Rate const &rate)
Definition: Rate2.cpp:87
STAmount accountFunds(ReadView const &view, AccountID const &id, STAmount const &saDefault, FreezeHandling freezeHandling, beast::Journal j)
Definition: View.cpp:442
@ fhZERO_IF_FROZEN
Definition: View.h:80
bool isXRP(AccountID const &c)
Definition: AccountID.h:91
Rate transferRate(ReadView const &view, AccountID const &issuer)
Returns IOU issuer transfer fee as Rate.
Definition: View.cpp:650
TER redeemIOU(ApplyView &view, AccountID const &account, STAmount const &amount, Issue const &issue, beast::Journal j)
Definition: View.cpp:1783
STAmount multiply(STAmount const &amount, Rate const &rate)
Definition: Rate2.cpp:47
TER transferXRP(ApplyView &view, AccountID const &from, AccountID const &to, STAmount const &amount, beast::Journal j)
Definition: View.cpp:1856
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:106
constexpr std::uint32_t tfPassive
Definition: TxFlags.h:96
@ tefINTERNAL
Definition: TER.h:173
static STAmount qual_mul(STAmount const &amount, Quality const &quality, STAmount const &output)
Definition: Taker.cpp:199
TER issueIOU(ApplyView &view, AccountID const &account, STAmount const &amount, Issue const &issue, beast::Journal j)
Definition: View.cpp:1683
static std::string format_amount(STAmount const &amount)
Definition: Taker.cpp:27
@ tesSUCCESS
Definition: TER.h:242
static STAmount qual_div(STAmount const &amount, Quality const &quality, STAmount const &output)
Definition: Taker.cpp:192
STAmount divRound(STAmount const &v1, STAmount const &v2, Asset const &asset, bool roundUp)
Definition: STAmount.cpp:1617
std::string to_string(base_uint< Bits, Tag > const &a)
Definition: base_uint.h:629
STAmount mulRound(STAmount const &v1, STAmount const &v2, Asset const &asset, bool roundUp)
Definition: STAmount.cpp:1510
constexpr std::uint32_t tfSell
Definition: TxFlags.h:99
CrossType
The flavor of an offer crossing.
Definition: Taker.h:36
Rate const parityRate
A transfer rate signifying a 1:1 exchange.
bool sanity_check() const
Definition: Taker.h:77
Represents a transfer rate.
Definition: Rate.h:38
std::uint32_t value
Definition: Rate.h:39