Taker::Taker (CrossType cross_type, ApplyView& view, AccountID const& account, Amounts const& offer, std::uint32_t flags, beast::Journal journal) : BasicTaker (cross_type, account, offer, Quality(offer), flags, calculateRate(view, offer.in.getIssuer(), account), calculateRate(view, offer.out.getIssuer(), account), journal) , view_ (view) , xrp_flow_ (0) , direct_crossings_ (0) , bridge_crossings_ (0) { assert (issue_in () == offer.in.issue ()); assert (issue_out () == offer.out.issue ()); if (journal_.debug) { journal_.debug << "Crossing as: " << to_string (account); if (isXRP (issue_in ())) journal_.debug << " Offer in: " << format_amount (offer.in); else journal_.debug << " Offer in: " << format_amount (offer.in) << " (issuer: " << issue_in ().account << ")"; if (isXRP (issue_out ())) journal_.debug << " Offer out: " << format_amount (offer.out); else journal_.debug << " Offer out: " << format_amount (offer.out) << " (issuer: " << issue_out ().account << ")"; journal_.debug << " Balance: " << format_amount (get_funds (account, offer.in)); } }
void attempt ( bool sell, std::string name, Quality taker_quality, cross_attempt_offer const offer, std::string const funds, Quality cross_quality, cross_attempt_offer const cross, std::string const cross_funds, cross_attempt_offer const flow, Issue const& issue_in, Issue const& issue_out, Rate rate_in = parityRate, Rate rate_out = parityRate) { Amounts taker_offer (parse_amounts ( offer.in, issue_in, offer.out, issue_out)); Amounts cross_offer (parse_amounts ( cross.in, issue_in, cross.out, issue_out)); CrossType cross_type; if (isXRP (issue_out)) cross_type = CrossType::IouToXrp; else if (isXRP (issue_in)) cross_type = CrossType::XrpToIou; else cross_type = CrossType::IouToIou; // FIXME: We are always invoking the IOU-to-IOU taker. We should select // the correct type dynamically. TestTaker taker (cross_type, taker_offer, taker_quality, parse_amount (funds, issue_in), sell ? tfSell : 0, rate_in, rate_out); taker.set_funds (parse_amount (cross_funds, issue_out)); auto result = taker.cross (cross_offer, cross_quality); Amounts const expected (parse_amounts ( flow.in, issue_in, flow.out, issue_out)); BEAST_EXPECT(expected == result); if (expected != result) { log << "Expected: " << format_amount (expected.in) << " : " << format_amount (expected.out) << '\n' << " Actual: " << format_amount (result.in) << " : " << format_amount (result.out) << std::endl; } }
void Taker::consume_offer (Offer const& offer, Amounts const& order) { if (order.in < zero) Throw<std::logic_error> ("flow with negative input."); if (order.out < zero) Throw<std::logic_error> ("flow with negative output."); if (journal_.debug) journal_.debug << "Consuming from offer " << offer; if (journal_.trace) { auto const& available = offer.amount (); journal_.trace << " in:" << format_amount (available.in); journal_.trace << " out:" << format_amount(available.out); } offer.consume (view_, order); }
void BasicTaker::log_flow (char const* description, Flow const& flow) { if (!journal_.debug) return; journal_.debug << description; if (isXRP (issue_in ())) journal_.debug << " order in: " << format_amount (flow.order.in); else journal_.debug << " order in: " << format_amount (flow.order.in) << " (issuer: " << format_amount (flow.issuers.in) << ")"; if (isXRP (issue_out ())) journal_.debug << " order out: " << format_amount (flow.order.out); else journal_.debug << " order out: " << format_amount (flow.order.out) << " (issuer: " << format_amount (flow.issuers.out) << ")"; }
// Calculates the bridged flow through the specified offers std::pair<BasicTaker::Flow, BasicTaker::Flow> BasicTaker::do_cross ( Amounts offer1, Quality quality1, AccountID const& owner1, Amounts offer2, Quality quality2, AccountID const& owner2) { assert (!done ()); assert (!offer1.in.native ()); assert (offer1.out.native ()); assert (offer2.in.native ()); assert (!offer2.out.native ()); // If the taker owns the first leg of the offer, then the taker's available // funds aren't the limiting factor for the input - the offer itself is. auto leg1_in_funds = get_funds (account (), offer1.in); if (account () == owner1) { journal_.trace << "The taker owns the first leg of a bridge."; leg1_in_funds = std::max (leg1_in_funds, offer1.in); } // If the taker owns the second leg of the offer, then the taker's available // funds are not the limiting factor for the output - the offer itself is. auto leg2_out_funds = get_funds (owner2, offer2.out); if (account () == owner2) { journal_.trace << "The taker owns the second leg of a bridge."; leg2_out_funds = std::max (leg2_out_funds, offer2.out); } // The amount available to flow via XRP is the amount that the owner of the // first leg of the bridge has, up to the first leg's output. // // But, when both legs of a bridge are owned by the same person, the amount // of XRP that can flow between the two legs is, essentially, infinite // since all the owner is doing is taking out XRP of his left pocket // and putting it in his right pocket. In that case, we set the available // XRP to the largest of the two offers. auto xrp_funds = get_funds (owner1, offer1.out); if (owner1 == owner2) { journal_.trace << "The bridge endpoints are owneb by the same account."; xrp_funds = std::max (offer1.out, offer2.in); } if (journal_.debug) { journal_.debug << "Available bridge funds:"; journal_.debug << " leg1 in: " << format_amount (leg1_in_funds); journal_.debug << " leg2 out: " << format_amount (leg2_out_funds); journal_.debug << " xrp: " << format_amount (xrp_funds); } auto const leg1_rate = in_rate (owner1, account ()); auto const leg2_rate = out_rate (owner2, account ()); // Attempt to determine the maximal flow that can be achieved across each // leg independent of the other. auto flow1 = flow_iou_to_xrp (offer1, quality1, xrp_funds, leg1_in_funds, leg1_rate); if (!flow1.sanity_check ()) Throw<std::logic_error> ("Computed flow1 fails sanity check."); auto flow2 = flow_xrp_to_iou (offer2, quality2, leg2_out_funds, xrp_funds, leg2_rate); if (!flow2.sanity_check ()) Throw<std::logic_error> ("Computed flow2 fails sanity check."); // We now have the maximal flows across each leg individually. We need to // equalize them, so that the amount of XRP that flows out of the first leg // is the same as the amount of XRP that flows into the second leg. We take // the side which is the limiting factor (if any) and adjust the other. if (flow1.order.out < flow2.order.in) { // Adjust the second leg of the offer down: flow2.order.in = flow1.order.out; flow2.order.out = qual_div (flow2.order.in, quality2, flow2.order.out); flow2.issuers.out = leg2_rate.multiply (flow2.order.out); log_flow ("Balancing: adjusted second leg down", flow2); } else if (flow1.order.out > flow2.order.in) { // Adjust the first leg of the offer down: flow1.order.out = flow2.order.in; flow1.order.in = qual_mul (flow1.order.out, quality1, flow1.order.in); flow1.issuers.in = leg1_rate.multiply (flow1.order.in); log_flow ("Balancing: adjusted first leg down", flow2); } if (flow1.order.out != flow2.order.in) Throw<std::logic_error> ("Bridged flow is out of balance."); remaining_.out -= flow2.order.out; remaining_.in -= flow1.order.in; return std::make_pair (flow1, flow2); }