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