std::pair<TER, core::Amounts> CreateOffer::crossOffersDirect ( core::LedgerView& view, core::Amounts const& taker_amount) { core::Taker::Options const options (mTxn.getFlags()); core::Clock::time_point const when ( mEngine->getLedger ()->getParentCloseTimeNC ()); core::LedgerView view_cancel (view.duplicate()); core::OfferStream offers ( view, view_cancel, Book (taker_amount.in.issue(), taker_amount.out.issue()), when, m_journal); core::Taker taker (offers.view(), mTxnAccountID, taker_amount, options); TER cross_result (tesSUCCESS); while (true) { // Modifying the order or logic of these // operations causes a protocol breaking change. // Checks which remove offers are performed early so we // can reduce the size of the order book as much as possible // before terminating the loop. if (taker.done()) { m_journal.debug << "The taker reports he's done during crossing!"; break; } if (! offers.step ()) { // Place the order since there are no // more offers and the order has a balance. m_journal.debug << "No more offers to consider during crossing!"; break; } auto const& offer (offers.tip()); if (taker.reject (offer.quality())) { // Place the order since there are no more offers // at the desired quality, and the order has a balance. break; } if (offer.account() == taker.account()) { // Skip offer from self. The offer will be considered expired and // will get deleted. continue; } if (m_journal.debug) m_journal.debug << " Offer: " << offer.entry()->getIndex() << std::endl << " " << offer.amount().in << " : " << offer.amount().out; cross_result = taker.cross (offer); if (cross_result != tesSUCCESS) { cross_result = tecFAILED_PROCESSING; break; } } return std::make_pair(cross_result, taker.remaining_offer ()); }
bool OfferStream::step () { // Modifying the order or logic of these // operations causes a protocol breaking change. for(;;) { // BookTip::step deletes the current offer from the view before // advancing to the next (unless the ledger entry is missing). if (! m_tip.step()) return false; SLE::pointer const& entry (m_tip.entry()); // Remove if missing if (! entry) { erase (view()); erase (view_cancel()); continue; } // Remove if expired if (entry->isFieldPresent (sfExpiration) && entry->getFieldU32 (sfExpiration) <= m_when) { view_cancel().offerDelete (entry->getIndex()); if (m_journal.trace) m_journal.trace << "Removing expired offer " << entry->getIndex(); continue; } m_offer = Offer (entry, m_tip.quality()); Amounts const amount (m_offer.amount()); // Remove if either amount is zero if (amount.empty()) { view_cancel().offerDelete (entry->getIndex()); if (m_journal.warning) m_journal.warning << "Removing bad offer " << entry->getIndex(); m_offer = Offer{}; continue; } // Calculate owner funds // NIKB NOTE The calling code also checks the funds, how expensive is // looking up the funds twice? Amount const owner_funds (view().accountFunds ( m_offer.account(), m_offer.amount().out, fhZERO_IF_FROZEN)); // Check for unfunded offer if (owner_funds <= zero) { // If the owner's balance in the pristine view is the same, // we haven't modified the balance and therefore the // offer is "found unfunded" versus "became unfunded" if (view_cancel().accountFunds (m_offer.account(), m_offer.amount().out, fhZERO_IF_FROZEN) == owner_funds) { view_cancel().offerDelete (entry->getIndex()); if (m_journal.trace) m_journal.trace << "Removing unfunded offer " << entry->getIndex(); } else { if (m_journal.trace) m_journal.trace << "Removing became unfunded offer " << entry->getIndex(); } m_offer = Offer{}; continue; } break; } return true; }