From 8f8b2ae4a3c0c3b49e006ac0b823745f08b279df Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Thu, 2 Jan 2014 03:35:34 -0800 Subject: [PATCH] Handle offers in quality directory but not in ledger --- src/ripple_app/misc/NetworkOPs.cpp | 156 ++++++------ src/ripple_app/paths/RippleCalc.cpp | 254 ++++++++++---------- src/ripple_app/tx/OfferCreateTransactor.cpp | 246 ++++++++++--------- 3 files changed, 340 insertions(+), 316 deletions(-) diff --git a/src/ripple_app/misc/NetworkOPs.cpp b/src/ripple_app/misc/NetworkOPs.cpp index 35ff52be7..96485891c 100644 --- a/src/ripple_app/misc/NetworkOPs.cpp +++ b/src/ripple_app/misc/NetworkOPs.cpp @@ -2827,99 +2827,107 @@ void NetworkOPsImp::getBookPage (Ledger::pointer lpLedger, const uint160& uTaker if (!bDone) { SLE::pointer sleOffer = lesActive.entryCache (ltOFFER, uOfferIndex); - const uint160 uOfferOwnerID = sleOffer->getFieldAccount160 (sfAccount); - const STAmount& saTakerGets = sleOffer->getFieldAmount (sfTakerGets); - const STAmount& saTakerPays = sleOffer->getFieldAmount (sfTakerPays); - STAmount saOwnerFunds; - if (uTakerGetsIssuerID == uOfferOwnerID) + if (sleOffer) { - // If offer is selling issuer's own IOUs, it is fully funded. - saOwnerFunds = saTakerGets; - } - else - { - std::map::const_iterator umBalanceEntry = umBalance.find (uOfferOwnerID); + const uint160 uOfferOwnerID = sleOffer->getFieldAccount160 (sfAccount); + const STAmount& saTakerGets = sleOffer->getFieldAmount (sfTakerGets); + const STAmount& saTakerPays = sleOffer->getFieldAmount (sfTakerPays); + STAmount saOwnerFunds; - if (umBalanceEntry != umBalance.end ()) + if (uTakerGetsIssuerID == uOfferOwnerID) { - // Found in running balance table. - - saOwnerFunds = umBalanceEntry->second; - // m_journal.info << boost::str(boost::format("getBookPage: saOwnerFunds=%s (cached)") % saOwnerFunds.getFullText()); + // If offer is selling issuer's own IOUs, it is fully funded. + saOwnerFunds = saTakerGets; } else { - // Did not find balance in table. + std::map::const_iterator umBalanceEntry = umBalance.find (uOfferOwnerID); - saOwnerFunds = lesActive.accountHolds (uOfferOwnerID, uTakerGetsCurrencyID, uTakerGetsIssuerID); - - // m_journal.info << boost::str(boost::format("getBookPage: saOwnerFunds=%s (new)") % saOwnerFunds.getFullText()); - if (saOwnerFunds.isNegative ()) + if (umBalanceEntry != umBalance.end ()) { - // Treat negative funds as zero. + // Found in running balance table. - saOwnerFunds.zero (); + saOwnerFunds = umBalanceEntry->second; + // m_journal.info << boost::str(boost::format("getBookPage: saOwnerFunds=%s (cached)") % saOwnerFunds.getFullText()); + } + else + { + // Did not find balance in table. + + saOwnerFunds = lesActive.accountHolds (uOfferOwnerID, uTakerGetsCurrencyID, uTakerGetsIssuerID); + + // m_journal.info << boost::str(boost::format("getBookPage: saOwnerFunds=%s (new)") % saOwnerFunds.getFullText()); + if (saOwnerFunds.isNegative ()) + { + // Treat negative funds as zero. + + saOwnerFunds.zero (); + } } } - } - Json::Value jvOffer = sleOffer->getJson (0); + Json::Value jvOffer = sleOffer->getJson (0); - STAmount saTakerGetsFunded; - STAmount saOwnerFundsLimit; - uint32 uOfferRate; + STAmount saTakerGetsFunded; + STAmount saOwnerFundsLimit; + uint32 uOfferRate; - if (uTransferRate != QUALITY_ONE // Have a tranfer fee. - && uTakerID != uTakerGetsIssuerID // Not taking offers of own IOUs. - && uTakerGetsIssuerID != uOfferOwnerID) // Offer owner not issuing ownfunds - { - // Need to charge a transfer fee to offer owner. - uOfferRate = uTransferRate; - saOwnerFundsLimit = STAmount::divide (saOwnerFunds, STAmount (CURRENCY_ONE, ACCOUNT_ONE, uOfferRate, -9)); + if (uTransferRate != QUALITY_ONE // Have a tranfer fee. + && uTakerID != uTakerGetsIssuerID // Not taking offers of own IOUs. + && uTakerGetsIssuerID != uOfferOwnerID) // Offer owner not issuing ownfunds + { + // Need to charge a transfer fee to offer owner. + uOfferRate = uTransferRate; + saOwnerFundsLimit = STAmount::divide (saOwnerFunds, STAmount (CURRENCY_ONE, ACCOUNT_ONE, uOfferRate, -9)); + } + else + { + uOfferRate = QUALITY_ONE; + saOwnerFundsLimit = saOwnerFunds; + } + + if (saOwnerFundsLimit >= saTakerGets) + { + // Sufficient funds no shenanigans. + saTakerGetsFunded = saTakerGets; + } + else + { + // m_journal.info << boost::str(boost::format("getBookPage: saTakerGets=%s") % saTakerGets.getFullText()); + // m_journal.info << boost::str(boost::format("getBookPage: saTakerPays=%s") % saTakerPays.getFullText()); + // m_journal.info << boost::str(boost::format("getBookPage: saOwnerFunds=%s") % saOwnerFunds.getFullText()); + // m_journal.info << boost::str(boost::format("getBookPage: saDirRate=%s") % saDirRate.getText()); + // m_journal.info << boost::str(boost::format("getBookPage: multiply=%s") % STAmount::multiply(saTakerGetsFunded, saDirRate).getFullText()); + // m_journal.info << boost::str(boost::format("getBookPage: multiply=%s") % STAmount::multiply(saTakerGetsFunded, saDirRate, saTakerPays).getFullText()); + + // Only provide, if not fully funded. + + saTakerGetsFunded = saOwnerFundsLimit; + + saTakerGetsFunded.setJson (jvOffer["taker_gets_funded"]); + std::min (saTakerPays, STAmount::multiply (saTakerGetsFunded, saDirRate, saTakerPays)).setJson (jvOffer["taker_pays_funded"]); + } + + STAmount saOwnerPays = (QUALITY_ONE == uOfferRate) + ? saTakerGetsFunded + : std::min (saOwnerFunds, STAmount::multiply (saTakerGetsFunded, STAmount (CURRENCY_ONE, ACCOUNT_ONE, uOfferRate, -9))); + + umBalance[uOfferOwnerID] = saOwnerFunds - saOwnerPays; + + if (!saOwnerFunds.isZero () || uOfferOwnerID == uTakerID) + { + // Only provide funded offers and offers of the taker. + Json::Value& jvOf = jvOffers.append (jvOffer); + jvOf["quality"] = saDirRate.getText (); + --iLeft; + } } else { - uOfferRate = QUALITY_ONE; - saOwnerFundsLimit = saOwnerFunds; - } - - if (saOwnerFundsLimit >= saTakerGets) - { - // Sufficient funds no shenanigans. - saTakerGetsFunded = saTakerGets; - } - else - { - // m_journal.info << boost::str(boost::format("getBookPage: saTakerGets=%s") % saTakerGets.getFullText()); - // m_journal.info << boost::str(boost::format("getBookPage: saTakerPays=%s") % saTakerPays.getFullText()); - // m_journal.info << boost::str(boost::format("getBookPage: saOwnerFunds=%s") % saOwnerFunds.getFullText()); - // m_journal.info << boost::str(boost::format("getBookPage: saDirRate=%s") % saDirRate.getText()); - // m_journal.info << boost::str(boost::format("getBookPage: multiply=%s") % STAmount::multiply(saTakerGetsFunded, saDirRate).getFullText()); - // m_journal.info << boost::str(boost::format("getBookPage: multiply=%s") % STAmount::multiply(saTakerGetsFunded, saDirRate, saTakerPays).getFullText()); - - // Only provide, if not fully funded. - - saTakerGetsFunded = saOwnerFundsLimit; - - saTakerGetsFunded.setJson (jvOffer["taker_gets_funded"]); - std::min (saTakerPays, STAmount::multiply (saTakerGetsFunded, saDirRate, saTakerPays)).setJson (jvOffer["taker_pays_funded"]); - } - - STAmount saOwnerPays = (QUALITY_ONE == uOfferRate) - ? saTakerGetsFunded - : std::min (saOwnerFunds, STAmount::multiply (saTakerGetsFunded, STAmount (CURRENCY_ONE, ACCOUNT_ONE, uOfferRate, -9))); - - umBalance[uOfferOwnerID] = saOwnerFunds - saOwnerPays; - - if (!saOwnerFunds.isZero () || uOfferOwnerID == uTakerID) - { - // Only provide funded offers and offers of the taker. - Json::Value& jvOf = jvOffers.append (jvOffer); - jvOf["quality"] = saDirRate.getText (); - --iLeft; - } + m_journal.warning << "Missing offer"; + } if (!lesActive.dirNext (uTipIndex, sleOfferDir, uBookEntry, uOfferIndex)) { diff --git a/src/ripple_app/paths/RippleCalc.cpp b/src/ripple_app/paths/RippleCalc.cpp index 4003e4414..5aee9ca2c 100644 --- a/src/ripple_app/paths/RippleCalc.cpp +++ b/src/ripple_app/paths/RippleCalc.cpp @@ -197,137 +197,145 @@ TER RippleCalc::calcNodeAdvance ( { // Got a new offer. sleOffer = lesActive.entryCache (ltOFFER, uOfferIndex); - uOfrOwnerID = sleOffer->getFieldAccount160 (sfAccount); - saTakerPays = sleOffer->getFieldAmount (sfTakerPays); - saTakerGets = sleOffer->getFieldAmount (sfTakerGets); - - const aciSource asLine = boost::make_tuple (uOfrOwnerID, uCurCurrencyID, uCurIssuerID); - - WriteLog (lsTRACE, RippleCalc) << boost::str (boost::format ("calcNodeAdvance: uOfrOwnerID=%s saTakerPays=%s saTakerGets=%s uOfferIndex=%s") - % RippleAddress::createHumanAccountID (uOfrOwnerID) - % saTakerPays - % saTakerGets - % uOfferIndex); - - if (sleOffer->isFieldPresent (sfExpiration) && sleOffer->getFieldU32 (sfExpiration) <= lesActive.getLedger ()->getParentCloseTimeNC ()) + if (!sleOffer) { - // Offer is expired. - WriteLog (lsTRACE, RippleCalc) << "calcNodeAdvance: expired offer"; - musUnfundedFound.insert(uOfferIndex); - continue; + WriteLog (lsWARNING, RippleCalc) << "Missing offer in directory"; + bEntryAdvance = true; } - else if (!saTakerPays.isPositive () || !saTakerGets.isPositive ()) + else { - // Offer has bad amounts. Offers should never have a bad amounts. + uOfrOwnerID = sleOffer->getFieldAccount160 (sfAccount); + saTakerPays = sleOffer->getFieldAmount (sfTakerPays); + saTakerGets = sleOffer->getFieldAmount (sfTakerGets); - if (bReverse) - { - // Past internal error, offer had bad amounts. - WriteLog (lsWARNING, RippleCalc) << boost::str (boost::format ("calcNodeAdvance: PAST INTERNAL ERROR: OFFER NON-POSITIVE: saTakerPays=%s saTakerGets=%s") - % saTakerPays % saTakerGets); + const aciSource asLine = boost::make_tuple (uOfrOwnerID, uCurCurrencyID, uCurIssuerID); - musUnfundedFound.insert (uOfferIndex); // Mark offer for always deletion. - continue; - } - else if (musUnfundedFound.find (uOfferIndex) != musUnfundedFound.end ()) - { - // Past internal error, offer was found failed to place this in musUnfundedFound. - WriteLog (lsDEBUG, RippleCalc) << boost::str (boost::format ("calcNodeAdvance: PAST INTERNAL ERROR: OFFER NON-POSITIVE: saTakerPays=%s saTakerGets=%s") - % saTakerPays % saTakerGets); - - // Just skip it. It will be deleted. - continue; - } - else - { - // Reverse should have previously put bad offer in list. - // An internal error previously left a bad offer. - WriteLog (lsWARNING, RippleCalc) << boost::str (boost::format ("calcNodeAdvance: INTERNAL ERROR: OFFER NON-POSITIVE: saTakerPays=%s saTakerGets=%s") - % saTakerPays % saTakerGets); - - // Don't process at all, things are in an unexpected state for this transactions. - terResult = tefEXCEPTION; - } - - // VFALCO NOTE What's the point of the earlier continue statements? - continue; - } - - // Allowed to access source from this node? - // XXX This can get called multiple times for same source in a row, caching result would be nice. - // XXX Going forward could we fund something with a worse quality which was previously skipped? Might need to check - // quality. - curIssuerNodeConstIterator itForward = psCur.umForward.find (asLine); - const bool bFoundForward = itForward != psCur.umForward.end (); - - // Only a allow a source to be used once, in the first node encountered from initial path scan. - // This prevents conflicting uses of the same balance when going reverse vs forward. - if (bFoundForward && (itForward->second != uNode) && (uOfrOwnerID != uCurIssuerID)) - { - // Temporarily unfunded. Another node uses this source, ignore in this offer. - WriteLog (lsTRACE, RippleCalc) << "calcNodeAdvance: temporarily unfunded offer (forward)"; - continue; - } - - // This is overly strict. For contributions to past. We should only count source if actually used. - curIssuerNodeConstIterator itReverse = psCur.umReverse.find (asLine); - bool bFoundReverse = itReverse != psCur.umReverse.end (); - - // For this quality increment, only allow a source to be used from a single node, in the first node encountered from applying offers - // in reverse. - if (bFoundReverse && (itReverse->second != uNode) && (uOfrOwnerID != uCurIssuerID)) - { - // Temporarily unfunded. Another node uses this source, ignore in this offer. - WriteLog (lsTRACE, RippleCalc) << "calcNodeAdvance: temporarily unfunded offer (reverse)"; - continue; - } - - // Determine if used in past. - // We only need to know if it might need to be marked unfunded. - curIssuerNodeConstIterator itPast = mumSource.find (asLine); - bool bFoundPast = itPast != mumSource.end (); - - // Only the current node is allowed to use the source. - - saOfferFunds = lesActive.accountFunds (uOfrOwnerID, saTakerGets); // Funds held. - - if (!saOfferFunds.isPositive ()) - { - // Offer is unfunded. - WriteLog (lsTRACE, RippleCalc) << "calcNodeAdvance: unfunded offer"; - - if (bReverse && !bFoundReverse && !bFoundPast) - { - // Never mentioned before, clearly just: found unfunded. - // That is, even if this offer fails due to fill or kill still do deletions. - musUnfundedFound.insert (uOfferIndex); // Mark offer for always deletion. - } - else - { - // Moving forward, don't need to insert again - // Or, already found it. - } - - // YYY Could verify offer is correct place for unfundeds. - continue; - } - - if (bReverse // Need to remember reverse mention. - && !bFoundPast // Not mentioned in previous passes. - && !bFoundReverse) // New to pass. - { - // Consider source mentioned by current path state. - WriteLog (lsTRACE, RippleCalc) << boost::str (boost::format ("calcNodeAdvance: remember=%s/%s/%s") + WriteLog (lsTRACE, RippleCalc) << boost::str (boost::format ("calcNodeAdvance: uOfrOwnerID=%s saTakerPays=%s saTakerGets=%s uOfferIndex=%s") % RippleAddress::createHumanAccountID (uOfrOwnerID) - % STAmount::createHumanCurrency (uCurCurrencyID) - % RippleAddress::createHumanAccountID (uCurIssuerID)); + % saTakerPays + % saTakerGets + % uOfferIndex); - psCur.umReverse.insert (std::make_pair (asLine, uNode)); + if (sleOffer->isFieldPresent (sfExpiration) && sleOffer->getFieldU32 (sfExpiration) <= lesActive.getLedger ()->getParentCloseTimeNC ()) + { + // Offer is expired. + WriteLog (lsTRACE, RippleCalc) << "calcNodeAdvance: expired offer"; + musUnfundedFound.insert(uOfferIndex); + continue; + } + else if (!saTakerPays.isPositive () || !saTakerGets.isPositive ()) + { + // Offer has bad amounts. Offers should never have a bad amounts. + + if (bReverse) + { + // Past internal error, offer had bad amounts. + WriteLog (lsWARNING, RippleCalc) << boost::str (boost::format ("calcNodeAdvance: PAST INTERNAL ERROR: OFFER NON-POSITIVE: saTakerPays=%s saTakerGets=%s") + % saTakerPays % saTakerGets); + + musUnfundedFound.insert (uOfferIndex); // Mark offer for always deletion. + continue; + } + else if (musUnfundedFound.find (uOfferIndex) != musUnfundedFound.end ()) + { + // Past internal error, offer was found failed to place this in musUnfundedFound. + WriteLog (lsDEBUG, RippleCalc) << boost::str (boost::format ("calcNodeAdvance: PAST INTERNAL ERROR: OFFER NON-POSITIVE: saTakerPays=%s saTakerGets=%s") + % saTakerPays % saTakerGets); + + // Just skip it. It will be deleted. + continue; + } + else + { + // Reverse should have previously put bad offer in list. + // An internal error previously left a bad offer. + WriteLog (lsWARNING, RippleCalc) << boost::str (boost::format ("calcNodeAdvance: INTERNAL ERROR: OFFER NON-POSITIVE: saTakerPays=%s saTakerGets=%s") + % saTakerPays % saTakerGets); + + // Don't process at all, things are in an unexpected state for this transactions. + terResult = tefEXCEPTION; + } + + // VFALCO NOTE What's the point of the earlier continue statements? + continue; + } + + // Allowed to access source from this node? + // XXX This can get called multiple times for same source in a row, caching result would be nice. + // XXX Going forward could we fund something with a worse quality which was previously skipped? Might need to check + // quality. + curIssuerNodeConstIterator itForward = psCur.umForward.find (asLine); + const bool bFoundForward = itForward != psCur.umForward.end (); + + // Only a allow a source to be used once, in the first node encountered from initial path scan. + // This prevents conflicting uses of the same balance when going reverse vs forward. + if (bFoundForward && (itForward->second != uNode) && (uOfrOwnerID != uCurIssuerID)) + { + // Temporarily unfunded. Another node uses this source, ignore in this offer. + WriteLog (lsTRACE, RippleCalc) << "calcNodeAdvance: temporarily unfunded offer (forward)"; + continue; + } + + // This is overly strict. For contributions to past. We should only count source if actually used. + curIssuerNodeConstIterator itReverse = psCur.umReverse.find (asLine); + bool bFoundReverse = itReverse != psCur.umReverse.end (); + + // For this quality increment, only allow a source to be used from a single node, in the first node encountered from applying offers + // in reverse. + if (bFoundReverse && (itReverse->second != uNode) && (uOfrOwnerID != uCurIssuerID)) + { + // Temporarily unfunded. Another node uses this source, ignore in this offer. + WriteLog (lsTRACE, RippleCalc) << "calcNodeAdvance: temporarily unfunded offer (reverse)"; + continue; + } + + // Determine if used in past. + // We only need to know if it might need to be marked unfunded. + curIssuerNodeConstIterator itPast = mumSource.find (asLine); + bool bFoundPast = itPast != mumSource.end (); + + // Only the current node is allowed to use the source. + + saOfferFunds = lesActive.accountFunds (uOfrOwnerID, saTakerGets); // Funds held. + + if (!saOfferFunds.isPositive ()) + { + // Offer is unfunded. + WriteLog (lsTRACE, RippleCalc) << "calcNodeAdvance: unfunded offer"; + + if (bReverse && !bFoundReverse && !bFoundPast) + { + // Never mentioned before, clearly just: found unfunded. + // That is, even if this offer fails due to fill or kill still do deletions. + musUnfundedFound.insert (uOfferIndex); // Mark offer for always deletion. + } + else + { + // Moving forward, don't need to insert again + // Or, already found it. + } + + // YYY Could verify offer is correct place for unfundeds. + continue; + } + + if (bReverse // Need to remember reverse mention. + && !bFoundPast // Not mentioned in previous passes. + && !bFoundReverse) // New to pass. + { + // Consider source mentioned by current path state. + WriteLog (lsTRACE, RippleCalc) << boost::str (boost::format ("calcNodeAdvance: remember=%s/%s/%s") + % RippleAddress::createHumanAccountID (uOfrOwnerID) + % STAmount::createHumanCurrency (uCurCurrencyID) + % RippleAddress::createHumanAccountID (uCurIssuerID)); + + psCur.umReverse.insert (std::make_pair (asLine, uNode)); + } + + bFundsDirty = false; + bEntryAdvance = false; } - - bFundsDirty = false; - bEntryAdvance = false; - } + } } while (tesSUCCESS == terResult && (bEntryAdvance || bDirectAdvance)); diff --git a/src/ripple_app/tx/OfferCreateTransactor.cpp b/src/ripple_app/tx/OfferCreateTransactor.cpp index d8f548385..1c78673e6 100644 --- a/src/ripple_app/tx/OfferCreateTransactor.cpp +++ b/src/ripple_app/tx/OfferCreateTransactor.cpp @@ -227,146 +227,154 @@ TER OfferCreateTransactor::takeOffers ( SLE::pointer sleOffer = mEngine->entryCache (ltOFFER, uOfferIndex); - WriteLog (lsDEBUG, OfferCreateTransactor) << "takeOffers: considering offer : " << sleOffer->getJson (0); - - const uint160 uOfferOwnerID = sleOffer->getFieldAccount160 (sfAccount); - STAmount saOfferPays = sleOffer->getFieldAmount (sfTakerGets); - STAmount saOfferGets = sleOffer->getFieldAmount (sfTakerPays); - - STAmount saOfferFunds; // Funds of offer owner to payout. - bool bValid; - - bValid = bValidOffer ( - sleOffer, uOfferIndex, uOfferOwnerID, saOfferPays, saOfferGets, - uTakerAccountID, - usOfferUnfundedFound, usOfferUnfundedBecame, usAccountTouched, - saOfferFunds); - - if (bValid) + if (sleOffer) { - STAmount saSubTakerPaid; - STAmount saSubTakerGot; - STAmount saTakerIssuerFee; - STAmount saOfferIssuerFee; - STAmount saOfferRate = STAmount::setRate (uTipQuality); + WriteLog (lsDEBUG, OfferCreateTransactor) << "takeOffers: considering offer : " << sleOffer->getJson (0); - WriteLog (lsTRACE, OfferCreateTransactor) << "takeOffers: applyOffer: saTakerPays: " << saTakerPays.getFullText (); - WriteLog (lsTRACE, OfferCreateTransactor) << "takeOffers: applyOffer: saTakerPaid: " << saTakerPaid.getFullText (); - WriteLog (lsTRACE, OfferCreateTransactor) << "takeOffers: applyOffer: saTakerFunds: " << saTakerFunds.getFullText (); - WriteLog (lsTRACE, OfferCreateTransactor) << "takeOffers: applyOffer: saOfferFunds: " << saOfferFunds.getFullText (); - WriteLog (lsTRACE, OfferCreateTransactor) << "takeOffers: applyOffer: saOfferPays: " << saOfferPays.getFullText (); - WriteLog (lsTRACE, OfferCreateTransactor) << "takeOffers: applyOffer: saOfferGets: " << saOfferGets.getFullText (); - WriteLog (lsTRACE, OfferCreateTransactor) << "takeOffers: applyOffer: saOfferRate: " << saOfferRate.getFullText (); - WriteLog (lsTRACE, OfferCreateTransactor) << "takeOffers: applyOffer: saSubTakerPays: " << saSubTakerPays.getFullText (); - WriteLog (lsTRACE, OfferCreateTransactor) << "takeOffers: applyOffer: saSubTakerGets: " << saSubTakerGets.getFullText (); - WriteLog (lsTRACE, OfferCreateTransactor) << "takeOffers: applyOffer: saTakerPays: " << saTakerPays.getFullText (); - WriteLog (lsTRACE, OfferCreateTransactor) << "takeOffers: applyOffer: saTakerGets: " << saTakerGets.getFullText (); + const uint160 uOfferOwnerID = sleOffer->getFieldAccount160 (sfAccount); + STAmount saOfferPays = sleOffer->getFieldAmount (sfTakerGets); + STAmount saOfferGets = sleOffer->getFieldAmount (sfTakerPays); - bool bOfferDelete = STAmount::applyOffer ( - bSell, - lesActive.rippleTransferRate (uTakerAccountID, uOfferOwnerID, uTakerPaysAccountID), - lesActive.rippleTransferRate (uOfferOwnerID, uTakerAccountID, uTakerGetsAccountID), - saOfferRate, - saOfferFunds, - saTakerFunds, - saOfferPays, - saOfferGets, - saSubTakerPays, - saSubTakerGets, - saSubTakerPaid, - saSubTakerGot, - saTakerIssuerFee, - saOfferIssuerFee); + STAmount saOfferFunds; // Funds of offer owner to payout. + bool bValid; - WriteLog (lsDEBUG, OfferCreateTransactor) << "takeOffers: applyOffer: saSubTakerPaid: " << saSubTakerPaid.getFullText (); - WriteLog (lsDEBUG, OfferCreateTransactor) << "takeOffers: applyOffer: saSubTakerGot: " << saSubTakerGot.getFullText (); + bValid = bValidOffer ( + sleOffer, uOfferIndex, uOfferOwnerID, saOfferPays, saOfferGets, + uTakerAccountID, + usOfferUnfundedFound, usOfferUnfundedBecame, usAccountTouched, + saOfferFunds); - // Adjust offer - - // Offer owner will pay less. Subtract what taker just got. - sleOffer->setFieldAmount (sfTakerGets, saOfferPays -= saSubTakerGot); - - // Offer owner will get less. Subtract what owner just paid. - sleOffer->setFieldAmount (sfTakerPays, saOfferGets -= saSubTakerPaid); - - mEngine->entryModify (sleOffer); - - if (bOfferDelete) + if (bValid) { - // Offer now fully claimed or now unfunded. - WriteLog (lsDEBUG, OfferCreateTransactor) << "takeOffers: Offer claimed: Delete."; + STAmount saSubTakerPaid; + STAmount saSubTakerGot; + STAmount saTakerIssuerFee; + STAmount saOfferIssuerFee; + STAmount saOfferRate = STAmount::setRate (uTipQuality); - usOfferUnfundedBecame.insert (uOfferIndex); // Delete unfunded offer on success. + WriteLog (lsTRACE, OfferCreateTransactor) << "takeOffers: applyOffer: saTakerPays: " << saTakerPays.getFullText (); + WriteLog (lsTRACE, OfferCreateTransactor) << "takeOffers: applyOffer: saTakerPaid: " << saTakerPaid.getFullText (); + WriteLog (lsTRACE, OfferCreateTransactor) << "takeOffers: applyOffer: saTakerFunds: " << saTakerFunds.getFullText (); + WriteLog (lsTRACE, OfferCreateTransactor) << "takeOffers: applyOffer: saOfferFunds: " << saOfferFunds.getFullText (); + WriteLog (lsTRACE, OfferCreateTransactor) << "takeOffers: applyOffer: saOfferPays: " << saOfferPays.getFullText (); + WriteLog (lsTRACE, OfferCreateTransactor) << "takeOffers: applyOffer: saOfferGets: " << saOfferGets.getFullText (); + WriteLog (lsTRACE, OfferCreateTransactor) << "takeOffers: applyOffer: saOfferRate: " << saOfferRate.getFullText (); + WriteLog (lsTRACE, OfferCreateTransactor) << "takeOffers: applyOffer: saSubTakerPays: " << saSubTakerPays.getFullText (); + WriteLog (lsTRACE, OfferCreateTransactor) << "takeOffers: applyOffer: saSubTakerGets: " << saSubTakerGets.getFullText (); + WriteLog (lsTRACE, OfferCreateTransactor) << "takeOffers: applyOffer: saTakerPays: " << saTakerPays.getFullText (); + WriteLog (lsTRACE, OfferCreateTransactor) << "takeOffers: applyOffer: saTakerGets: " << saTakerGets.getFullText (); - // Offer owner's account is no longer pristine. - usAccountTouched.insert (uOfferOwnerID); - } - else if (saSubTakerGot) - { - WriteLog (lsDEBUG, OfferCreateTransactor) << "takeOffers: Offer partial claim."; + bool bOfferDelete = STAmount::applyOffer ( + bSell, + lesActive.rippleTransferRate (uTakerAccountID, uOfferOwnerID, uTakerPaysAccountID), + lesActive.rippleTransferRate (uOfferOwnerID, uTakerAccountID, uTakerGetsAccountID), + saOfferRate, + saOfferFunds, + saTakerFunds, + saOfferPays, + saOfferGets, + saSubTakerPays, + saSubTakerGets, + saSubTakerPaid, + saSubTakerGot, + saTakerIssuerFee, + saOfferIssuerFee); - if (!saOfferPays.isPositive () || !saOfferGets.isPositive ()) + WriteLog (lsDEBUG, OfferCreateTransactor) << "takeOffers: applyOffer: saSubTakerPaid: " << saSubTakerPaid.getFullText (); + WriteLog (lsDEBUG, OfferCreateTransactor) << "takeOffers: applyOffer: saSubTakerGot: " << saSubTakerGot.getFullText (); + + // Adjust offer + + // Offer owner will pay less. Subtract what taker just got. + sleOffer->setFieldAmount (sfTakerGets, saOfferPays -= saSubTakerGot); + + // Offer owner will get less. Subtract what owner just paid. + sleOffer->setFieldAmount (sfTakerPays, saOfferGets -= saSubTakerPaid); + + mEngine->entryModify (sleOffer); + + if (bOfferDelete) { - WriteLog (lsWARNING, OfferCreateTransactor) << "takeOffers: ILLEGAL OFFER RESULT."; - bUnfunded = true; - terResult = bOpenLedger ? telFAILED_PROCESSING : tecFAILED_PROCESSING; + // Offer now fully claimed or now unfunded. + WriteLog (lsDEBUG, OfferCreateTransactor) << "takeOffers: Offer claimed: Delete."; + + usOfferUnfundedBecame.insert (uOfferIndex); // Delete unfunded offer on success. + + // Offer owner's account is no longer pristine. + usAccountTouched.insert (uOfferOwnerID); } - } - else - { - // Taker got nothing, probably due to rounding. Consider taker unfunded. - WriteLog (lsDEBUG, OfferCreateTransactor) << "takeOffers: No claim."; - - bUnfunded = true; - terResult = tesSUCCESS; // Done. - } - - assert (uTakerGetsAccountID == saSubTakerGot.getIssuer ()); - assert (uTakerPaysAccountID == saSubTakerPaid.getIssuer ()); - - if (!bUnfunded) - { - // Distribute funds. The sends charge appropriate fees which are implied by offer. - - terResult = lesActive.accountSend (uOfferOwnerID, uTakerAccountID, saSubTakerGot); // Offer owner pays taker. - - if (tesSUCCESS == terResult) - terResult = lesActive.accountSend (uTakerAccountID, uOfferOwnerID, saSubTakerPaid); // Taker pays offer owner. - - if (bSell) + else if (saSubTakerGot) { - // Sell semantics: - // Reduce amount considered received to original offer's rate. - // Not by crossing rate, which is higher. - STAmount saEffectiveGot = STAmount::divide(saSubTakerPaid, saTakerRate, saTakerGets); - saSubTakerGot = std::min(saEffectiveGot, saSubTakerGot); + WriteLog (lsDEBUG, OfferCreateTransactor) << "takeOffers: Offer partial claim."; + + if (!saOfferPays.isPositive () || !saOfferGets.isPositive ()) + { + WriteLog (lsWARNING, OfferCreateTransactor) << "takeOffers: ILLEGAL OFFER RESULT."; + bUnfunded = true; + terResult = bOpenLedger ? telFAILED_PROCESSING : tecFAILED_PROCESSING; + } } else { - // Buy semantics: Reduce amount considered paid by taker's rate. Not by actual cost which is lower. - // That is, take less as to just satify our buy requirement. - STAmount saTakerCould = saTakerPays - saTakerPaid; // Taker could pay. + // Taker got nothing, probably due to rounding. Consider taker unfunded. + WriteLog (lsDEBUG, OfferCreateTransactor) << "takeOffers: No claim."; - if (saTakerFunds < saTakerCould) - saTakerCould = saTakerFunds; - - STAmount saTakerUsed = STAmount::multiply (saSubTakerGot, saTakerRate, saTakerPays); - - WriteLog (lsDEBUG, OfferCreateTransactor) << "takeOffers: applyOffer: saTakerCould: " << saTakerCould.getFullText (); - WriteLog (lsDEBUG, OfferCreateTransactor) << "takeOffers: applyOffer: saSubTakerGot: " << saSubTakerGot.getFullText (); - WriteLog (lsDEBUG, OfferCreateTransactor) << "takeOffers: applyOffer: saTakerRate: " << saTakerRate.getFullText (); - WriteLog (lsDEBUG, OfferCreateTransactor) << "takeOffers: applyOffer: saTakerUsed: " << saTakerUsed.getFullText (); - - saSubTakerPaid = std::min (saTakerCould, saTakerUsed); + bUnfunded = true; + terResult = tesSUCCESS; // Done. } - saTakerPaid += saSubTakerPaid; - saTakerGot += saSubTakerGot; + assert (uTakerGetsAccountID == saSubTakerGot.getIssuer ()); + assert (uTakerPaysAccountID == saSubTakerPaid.getIssuer ()); - if (tesSUCCESS == terResult) - terResult = temUNCERTAIN; + if (!bUnfunded) + { + // Distribute funds. The sends charge appropriate fees which are implied by offer. + + terResult = lesActive.accountSend (uOfferOwnerID, uTakerAccountID, saSubTakerGot); // Offer owner pays taker. + + if (tesSUCCESS == terResult) + terResult = lesActive.accountSend (uTakerAccountID, uOfferOwnerID, saSubTakerPaid); // Taker pays offer owner. + + if (bSell) + { + // Sell semantics: + // Reduce amount considered received to original offer's rate. + // Not by crossing rate, which is higher. + STAmount saEffectiveGot = STAmount::divide(saSubTakerPaid, saTakerRate, saTakerGets); + saSubTakerGot = std::min(saEffectiveGot, saSubTakerGot); + } + else + { + // Buy semantics: Reduce amount considered paid by taker's rate. Not by actual cost which is lower. + // That is, take less as to just satify our buy requirement. + STAmount saTakerCould = saTakerPays - saTakerPaid; // Taker could pay. + + if (saTakerFunds < saTakerCould) + saTakerCould = saTakerFunds; + + STAmount saTakerUsed = STAmount::multiply (saSubTakerGot, saTakerRate, saTakerPays); + + WriteLog (lsDEBUG, OfferCreateTransactor) << "takeOffers: applyOffer: saTakerCould: " << saTakerCould.getFullText (); + WriteLog (lsDEBUG, OfferCreateTransactor) << "takeOffers: applyOffer: saSubTakerGot: " << saSubTakerGot.getFullText (); + WriteLog (lsDEBUG, OfferCreateTransactor) << "takeOffers: applyOffer: saTakerRate: " << saTakerRate.getFullText (); + WriteLog (lsDEBUG, OfferCreateTransactor) << "takeOffers: applyOffer: saTakerUsed: " << saTakerUsed.getFullText (); + + saSubTakerPaid = std::min (saTakerCould, saTakerUsed); + } + + saTakerPaid += saSubTakerPaid; + saTakerGot += saSubTakerGot; + + if (tesSUCCESS == terResult) + terResult = temUNCERTAIN; + } } } + else + { + WriteLog (lsWARNING, OfferCreateTransactor) << "missing offer"; + // WRITEME: Remove the missing offer from the directory + } } }