mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-20 02:55:50 +00:00
* Set enforce date: September 15, 2014 * Enforce in stand alone mode * Enforce at source * Enforce intermediary nodes * Enforce global freeze in get paths out * Enforce global freeze in create offer * Don't consider frozen links a path out * Handle in getBookPage * Enforce in new offer transactors
196 lines
6.8 KiB
C++
196 lines
6.8 KiB
C++
//------------------------------------------------------------------------------
|
|
/*
|
|
This file is part of rippled: https://github.com/ripple/rippled
|
|
Copyright (c) 2012, 2013 Ripple Labs Inc.
|
|
|
|
Permission to use, copy, modify, and/or distribute this software for any
|
|
purpose with or without fee is hereby granted, provided that the above
|
|
copyright notice and this permission notice appear in all copies.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
//==============================================================================
|
|
|
|
#include <ripple/module/app/book/OfferStream.h>
|
|
#include <ripple/module/app/book/Taker.h>
|
|
#include <ripple/module/app/book/Quality.h>
|
|
#include <beast/streams/debug_ostream.h>
|
|
|
|
namespace ripple {
|
|
|
|
std::pair<TER, core::Amounts>
|
|
CreateOfferBridged::crossOffers (
|
|
core::LedgerView& view,
|
|
core::Amounts const& taker_amount)
|
|
{
|
|
assert (!taker_amount.in.isNative () && !taker_amount.out.isNative ());
|
|
|
|
if (taker_amount.in.isNative () || taker_amount.out.isNative ())
|
|
return std::make_pair (tefINTERNAL, core::Amounts ());
|
|
|
|
core::Clock::time_point const when (
|
|
mEngine->getLedger ()->getParentCloseTimeNC ());
|
|
|
|
core::Taker::Options const options (mTxn.getFlags());
|
|
|
|
core::LedgerView view_cancel (view.duplicate());
|
|
|
|
auto& asset_in = taker_amount.in.issue();
|
|
auto& asset_out = taker_amount.out.issue();
|
|
|
|
core::OfferStream offers_direct (view, view_cancel,
|
|
Book (asset_in, asset_out), when, m_journal);
|
|
|
|
core::OfferStream offers_leg1 (view, view_cancel,
|
|
Book (asset_in, xrpIssue ()), when, m_journal);
|
|
|
|
core::OfferStream offers_leg2 (view, view_cancel,
|
|
Book (xrpIssue (), asset_out), when, m_journal);
|
|
|
|
core::Taker taker (view, mTxnAccountID, taker_amount, options);
|
|
|
|
if (m_journal.debug) m_journal.debug <<
|
|
"process_order: " <<
|
|
(options.sell? "sell" : "buy") << " " <<
|
|
(options.passive? "passive" : "") << std::endl <<
|
|
" taker: " << taker.account() << std::endl <<
|
|
" balances: " <<
|
|
view.accountFunds (taker.account(), taker_amount.in, fhIGNORE_FREEZE)
|
|
<< ", " <<
|
|
view.accountFunds (taker.account(), taker_amount.out, fhIGNORE_FREEZE);
|
|
|
|
TER cross_result (tesSUCCESS);
|
|
|
|
/* Note the subtle distinction here: self-offers encountered in the bridge
|
|
* are taken, but self-offers encountered in the direct book are not.
|
|
*/
|
|
bool have_bridged (offers_leg1.step () && offers_leg2.step ());
|
|
bool have_direct (offers_direct.step_account (taker.account ()));
|
|
bool place_order (true);
|
|
|
|
while (have_direct || have_bridged)
|
|
{
|
|
core::Quality quality;
|
|
bool use_direct;
|
|
bool leg1_consumed(false);
|
|
bool leg2_consumed(false);
|
|
bool direct_consumed(false);
|
|
|
|
// Logic:
|
|
// We calculate the qualities of any direct and bridged offers at the
|
|
// tip of the order book, and choose the best one of the two.
|
|
|
|
if (have_direct)
|
|
{
|
|
core::Quality const direct_quality (offers_direct.tip ().quality ());
|
|
|
|
if (have_bridged)
|
|
{
|
|
core::Quality const bridged_quality (core::composed_quality (
|
|
offers_leg1.tip ().quality (),
|
|
offers_leg2.tip ().quality ()));
|
|
|
|
if (bridged_quality < direct_quality)
|
|
{
|
|
use_direct = true;
|
|
quality = direct_quality;
|
|
}
|
|
else
|
|
{
|
|
use_direct = false;
|
|
quality = bridged_quality;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
use_direct = true;
|
|
quality = offers_direct.tip ().quality ();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
use_direct = false;
|
|
quality = core::composed_quality (
|
|
offers_leg1.tip ().quality (),
|
|
offers_leg2.tip ().quality ());
|
|
}
|
|
|
|
// We are always looking at the best quality available, so if we reject
|
|
// that, we know that we are done.
|
|
if (taker.reject(quality))
|
|
break;
|
|
|
|
if (use_direct)
|
|
{
|
|
if (m_journal.debug) m_journal.debug << "Direct:" << std::endl <<
|
|
" Offer: " << offers_direct.tip () << std::endl <<
|
|
" " << offers_direct.tip ().amount().in <<
|
|
" : " << offers_direct.tip ().amount ().out;
|
|
|
|
cross_result = taker.cross(offers_direct.tip ());
|
|
|
|
if (offers_direct.tip ().fully_consumed ())
|
|
{
|
|
direct_consumed = true;
|
|
have_direct = offers_direct.step_account (taker.account());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (m_journal.debug) m_journal.debug << "Bridge:" << std::endl <<
|
|
" Offer1: " << offers_leg1.tip () << std::endl <<
|
|
" " << offers_leg1.tip ().amount().in <<
|
|
" : " << offers_leg1.tip ().amount ().out << std::endl <<
|
|
" Offer2: " << offers_leg2.tip () << std::endl <<
|
|
" " << offers_leg2.tip ().amount ().in <<
|
|
" : " << offers_leg2.tip ().amount ().out;
|
|
|
|
cross_result = taker.cross(offers_leg1.tip (), offers_leg2.tip ());
|
|
|
|
if (offers_leg1.tip ().fully_consumed ())
|
|
{
|
|
leg1_consumed = true;
|
|
have_bridged = offers_leg1.step ();
|
|
}
|
|
if (have_bridged && offers_leg2.tip ().fully_consumed ())
|
|
{
|
|
leg2_consumed = true;
|
|
have_bridged = offers_leg2.step ();
|
|
}
|
|
}
|
|
|
|
if (cross_result != tesSUCCESS)
|
|
{
|
|
cross_result = tecFAILED_PROCESSING;
|
|
break;
|
|
}
|
|
|
|
if (taker.done())
|
|
{
|
|
m_journal.debug << "The taker reports he's done during crossing!";
|
|
place_order = false;
|
|
break;
|
|
}
|
|
|
|
// Postcondition: If we aren't done, then we *must* have consumed at
|
|
// least one offer fully.
|
|
assert (direct_consumed || leg1_consumed || leg2_consumed);
|
|
|
|
if (!direct_consumed && !leg1_consumed && !leg2_consumed)
|
|
{
|
|
cross_result = tefINTERNAL;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return std::make_pair(cross_result, taker.remaining_offer ());
|
|
}
|
|
|
|
}
|