From 4ac824419a89a23800b5bd4926ddc9ce535ea6fb Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Thu, 21 Mar 2013 16:27:24 -0700 Subject: [PATCH] Abstract valid offer testing in OfferCreate. --- src/cpp/ripple/OfferCreateTransactor.cpp | 309 +++++++++++++---------- src/cpp/ripple/OfferCreateTransactor.h | 12 + 2 files changed, 183 insertions(+), 138 deletions(-) diff --git a/src/cpp/ripple/OfferCreateTransactor.cpp b/src/cpp/ripple/OfferCreateTransactor.cpp index 686b21e832..0743b36380 100644 --- a/src/cpp/ripple/OfferCreateTransactor.cpp +++ b/src/cpp/ripple/OfferCreateTransactor.cpp @@ -7,6 +7,80 @@ SETUP_LOG(); +// Make sure an offer is still valid. If not, mark it unfunded. +bool OfferCreateTransactor::bValidOffer( + SLE::ref sleOfferDir, + const uint256& uOfferIndex, + const uint160& uOfferOwnerID, + const STAmount& saOfferPays, + const STAmount& saOfferGets, + const uint160& uTakerAccountID, + boost::unordered_set& usOfferUnfundedFound, + boost::unordered_set& usOfferUnfundedBecame, + boost::unordered_set& usAccountTouched, + STAmount& saOfferFunds) { // <-- + bool bValid; + + if (sleOfferDir->isFieldPresent(sfExpiration) && sleOfferDir->getFieldU32(sfExpiration) <= mEngine->getLedger()->getParentCloseTimeNC()) + { + // Offer is expired. Expired offers are considered unfunded. Delete it. + cLog(lsINFO) << "bValidOffer: encountered expired offer"; + + usOfferUnfundedFound.insert(uOfferIndex); + + bValid = false; + } + else if (uOfferOwnerID == uTakerAccountID) + { + // Would take own offer. Consider old offer expired. Delete it. + cLog(lsINFO) << "bValidOffer: encountered taker's own old offer"; + + usOfferUnfundedFound.insert(uOfferIndex); + + bValid = false; + } + else if (!saOfferGets.isPositive() || !saOfferPays.isPositive()) + { + // Offer has bad amounts. Consider offer expired. Delete it. + cLog(lsWARNING) << boost::str(boost::format("bValidOffer: BAD OFFER: saOfferPays=%s saOfferGets=%s") + % saOfferPays % saOfferGets); + + usOfferUnfundedFound.insert(uOfferIndex); + } + else + { + cLog(lsINFO) << "bValidOffer: saOfferPays=" << saOfferPays.getFullText(); + + saOfferFunds = mEngine->getNodes().accountFunds(uOfferOwnerID, saOfferPays); + + if (!saOfferFunds.isPositive()) + { + // Offer is unfunded, possibly due to previous balance action. + cLog(lsINFO) << "bValidOffer: offer unfunded: delete"; + + boost::unordered_set::iterator account = usAccountTouched.find(uOfferOwnerID); + if (account != usAccountTouched.end()) + { + // Previously touched account. + usOfferUnfundedBecame.insert(uOfferIndex); // Delete unfunded offer on success. + } + else + { + // Never touched source account. + usOfferUnfundedFound.insert(uOfferIndex); // Delete found unfunded offer when possible. + } + + bValid = false; + } + else + { + bValid = true; + } + } + + return bValid; +} + // Take as much as possible. Adjusts account balances. Charges fees on top to taker. // --> uBookBase: The order book to take against. // --> saTakerPays: What the taker offers (w/ issuer) @@ -28,7 +102,7 @@ TER OfferCreateTransactor::takeOffers( bool& bUnfunded) { // The book has the most elements. Take the perspective of the book. - // Book is ordered for taker: taker pays / taker gets, < is better + // Book is ordered for taker: taker pays / taker gets (smaller is better) // The order is for the other books currencys for get and pays are opposites. // We want the same ratio for the respective currencies. @@ -141,162 +215,121 @@ TER OfferCreateTransactor::takeOffers( STAmount saOfferPays = sleOffer->getFieldAmount(sfTakerGets); STAmount saOfferGets = sleOffer->getFieldAmount(sfTakerPays); - if (sleOffer->isFieldPresent(sfExpiration) && sleOffer->getFieldU32(sfExpiration) <= mEngine->getLedger()->getParentCloseTimeNC()) - { - // Offer is expired. Expired offers are considered unfunded. Delete it. - cLog(lsINFO) << "takeOffers: encountered expired offer"; + STAmount saOfferFunds; // Funds of offer owner to payout. + bool bValid; - usOfferUnfundedFound.insert(uOfferIndex); - } - else if (uOfferOwnerID == uTakerAccountID) - { - // Would take own offer. Consider old offer expired. Delete it. - cLog(lsINFO) << "takeOffers: encountered taker's own old offer"; + bValid = bValidOffer( + sleOfferDir, uOfferIndex, uOfferOwnerID, saOfferPays, saOfferGets, + uTakerAccountID, + usOfferUnfundedFound, usOfferUnfundedBecame, usAccountTouched, + saOfferFunds); - usOfferUnfundedBecame.insert(uOfferIndex); - } - else if (!saOfferGets.isPositive() || !saOfferPays.isPositive()) - { - // Offer has bad amounts. Consider offer expired. Delete it. - cLog(lsWARNING) << boost::str(boost::format("takeOffers: BAD OFFER: saOfferPays=%s saOfferGets=%s") - % saOfferPays % saOfferGets); + if (bValid) { + STAmount saSubTakerPaid; + STAmount saSubTakerGot; + STAmount saTakerIssuerFee; + STAmount saOfferIssuerFee; + STAmount saOfferRate = STAmount::setRate(uTipQuality); - usOfferUnfundedFound.insert(uOfferIndex); - } - else - { - // Get offer funds available. + cLog(lsINFO) << "takeOffers: applyOffer: saTakerPays: " << saTakerPays.getFullText(); + cLog(lsINFO) << "takeOffers: applyOffer: saTakerPaid: " << saTakerPaid.getFullText(); + cLog(lsINFO) << "takeOffers: applyOffer: saTakerFunds: " << saTakerFunds.getFullText(); + cLog(lsINFO) << "takeOffers: applyOffer: saOfferFunds: " << saOfferFunds.getFullText(); + cLog(lsINFO) << "takeOffers: applyOffer: saOfferPays: " << saOfferPays.getFullText(); + cLog(lsINFO) << "takeOffers: applyOffer: saOfferGets: " << saOfferGets.getFullText(); + cLog(lsINFO) << "takeOffers: applyOffer: saOfferRate: " << saOfferRate.getFullText(); + cLog(lsINFO) << "takeOffers: applyOffer: saSubTakerPays: " << saSubTakerPays.getFullText(); + cLog(lsINFO) << "takeOffers: applyOffer: saSubTakerGets: " << saSubTakerGets.getFullText(); + cLog(lsINFO) << "takeOffers: applyOffer: saTakerPays: " << saTakerPays.getFullText(); + cLog(lsINFO) << "takeOffers: applyOffer: saTakerGets: " << saTakerGets.getFullText(); - cLog(lsINFO) << "takeOffers: saOfferPays=" << saOfferPays.getFullText(); + bool bOfferDelete = STAmount::applyOffer( + lesActive.rippleTransferRate(uTakerAccountID, uOfferOwnerID, uTakerPaysAccountID), + lesActive.rippleTransferRate(uOfferOwnerID, uTakerAccountID, uTakerGetsAccountID), + saOfferRate, + saOfferFunds, + saTakerFunds, + saOfferPays, + saOfferGets, + saSubTakerPays, + saSubTakerGets, + saSubTakerPaid, + saSubTakerGot, + saTakerIssuerFee, + saOfferIssuerFee); - STAmount saOfferFunds = lesActive.accountFunds(uOfferOwnerID, saOfferPays); - SLE::pointer sleOfferAccount; // Owner of offer. + cLog(lsINFO) << "takeOffers: applyOffer: saSubTakerPaid: " << saSubTakerPaid.getFullText(); + cLog(lsINFO) << "takeOffers: applyOffer: saSubTakerGot: " << saSubTakerGot.getFullText(); - if (!saOfferFunds.isPositive()) // Includes zero. + // 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) { - // Offer is unfunded, possibly due to previous balance action. - cLog(lsINFO) << "takeOffers: offer unfunded: delete"; + // Offer now fully claimed or now unfunded. + cLog(lsINFO) << "takeOffers: Offer claimed: Delete."; - boost::unordered_set::iterator account = usAccountTouched.find(uOfferOwnerID); - if (account != usAccountTouched.end()) + usOfferUnfundedBecame.insert(uOfferIndex); // Delete unfunded offer on success. + + // Offer owner's account is no longer pristine. + usAccountTouched.insert(uOfferOwnerID); + } + else if (saSubTakerGot) + { + cLog(lsINFO) << "takeOffers: Offer partial claim."; + + if (!saOfferPays.isPositive() || !saOfferGets.isPositive()) { - // Previously touched account. - usOfferUnfundedBecame.insert(uOfferIndex); // Delete unfunded offer on success. - } - else - { - // Never touched source account. - usOfferUnfundedFound.insert(uOfferIndex); // Delete found unfunded offer when possible. + cLog(lsWARNING) << "takeOffers: ILLEGAL OFFER RESULT."; + bUnfunded = true; + terResult = bOpenLedger ? telFAILED_PROCESSING : tecFAILED_PROCESSING; } } else { - STAmount saSubTakerPaid; - STAmount saSubTakerGot; - STAmount saTakerIssuerFee; - STAmount saOfferIssuerFee; - STAmount saOfferRate = STAmount::setRate(uTipQuality); + // Taker got nothing, probably due to rounding. Consider taker unfunded. + cLog(lsINFO) << "takeOffers: No claim."; - cLog(lsINFO) << "takeOffers: applyOffer: saTakerPays: " << saTakerPays.getFullText(); - cLog(lsINFO) << "takeOffers: applyOffer: saTakerPaid: " << saTakerPaid.getFullText(); - cLog(lsINFO) << "takeOffers: applyOffer: saTakerFunds: " << saTakerFunds.getFullText(); - cLog(lsINFO) << "takeOffers: applyOffer: saOfferFunds: " << saOfferFunds.getFullText(); - cLog(lsINFO) << "takeOffers: applyOffer: saOfferPays: " << saOfferPays.getFullText(); - cLog(lsINFO) << "takeOffers: applyOffer: saOfferGets: " << saOfferGets.getFullText(); - cLog(lsINFO) << "takeOffers: applyOffer: saOfferRate: " << saOfferRate.getFullText(); - cLog(lsINFO) << "takeOffers: applyOffer: saSubTakerPays: " << saSubTakerPays.getFullText(); - cLog(lsINFO) << "takeOffers: applyOffer: saSubTakerGets: " << saSubTakerGets.getFullText(); - cLog(lsINFO) << "takeOffers: applyOffer: saTakerPays: " << saTakerPays.getFullText(); - cLog(lsINFO) << "takeOffers: applyOffer: saTakerGets: " << saTakerGets.getFullText(); + bUnfunded = true; + terResult = tesSUCCESS; // Done. + } - bool bOfferDelete = STAmount::applyOffer( - lesActive.rippleTransferRate(uTakerAccountID, uOfferOwnerID, uTakerPaysAccountID), - lesActive.rippleTransferRate(uOfferOwnerID, uTakerAccountID, uTakerGetsAccountID), - saOfferRate, - saOfferFunds, - saTakerFunds, - saOfferPays, - saOfferGets, - saSubTakerPays, - saSubTakerGets, - saSubTakerPaid, - saSubTakerGot, - saTakerIssuerFee, - saOfferIssuerFee); + assert(uTakerGetsAccountID == saSubTakerGot.getIssuer()); + assert(uTakerPaysAccountID == saSubTakerPaid.getIssuer()); - cLog(lsINFO) << "takeOffers: applyOffer: saSubTakerPaid: " << saSubTakerPaid.getFullText(); + 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. + + // Reduce amount considered paid by taker's rate (not actual cost). + STAmount saTakerCould = saTakerPays - saTakerPaid; // Taker could pay. + if (saTakerFunds < saTakerCould) + saTakerCould = saTakerFunds; + + STAmount saTakerUsed = STAmount::multiply(saSubTakerGot, saTakerRate, saTakerPays); + + cLog(lsINFO) << "takeOffers: applyOffer: saTakerCould: " << saTakerCould.getFullText(); cLog(lsINFO) << "takeOffers: applyOffer: saSubTakerGot: " << saSubTakerGot.getFullText(); + cLog(lsINFO) << "takeOffers: applyOffer: saTakerRate: " << saTakerRate.getFullText(); + cLog(lsINFO) << "takeOffers: applyOffer: saTakerUsed: " << saTakerUsed.getFullText(); - // Adjust offer + saTakerPaid += std::min(saTakerCould, saTakerUsed); + saTakerGot += saSubTakerGot; - // 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) - { - // Offer now fully claimed or now unfunded. - cLog(lsINFO) << "takeOffers: Offer claimed: Delete."; - - usOfferUnfundedBecame.insert(uOfferIndex); // Delete unfunded offer on success. - - // Offer owner's account is no longer pristine. - usAccountTouched.insert(uOfferOwnerID); - } - else if (saSubTakerGot) - { - cLog(lsINFO) << "takeOffers: Offer partial claim."; - - if (!saOfferPays.isPositive() || !saOfferGets.isPositive()) - { - cLog(lsWARNING) << "takeOffers: ILLEGAL OFFER RESULT."; - bUnfunded = true; - terResult = bOpenLedger ? telFAILED_PROCESSING : tecFAILED_PROCESSING; - } - } - else - { - // Taker got nothing, probably due to rounding. Consider taker unfunded. - cLog(lsINFO) << "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. - - // Reduce amount considered paid by taker's rate (not actual cost). - STAmount saTakerCould = saTakerPays - saTakerPaid; // Taker could pay. - if (saTakerFunds < saTakerCould) - saTakerCould = saTakerFunds; - - STAmount saTakerUsed = STAmount::multiply(saSubTakerGot, saTakerRate, saTakerPays); - - cLog(lsINFO) << "takeOffers: applyOffer: saTakerCould: " << saTakerCould.getFullText(); - cLog(lsINFO) << "takeOffers: applyOffer: saSubTakerGot: " << saSubTakerGot.getFullText(); - cLog(lsINFO) << "takeOffers: applyOffer: saTakerRate: " << saTakerRate.getFullText(); - cLog(lsINFO) << "takeOffers: applyOffer: saTakerUsed: " << saTakerUsed.getFullText(); - - saTakerPaid += std::min(saTakerCould, saTakerUsed); - saTakerGot += saSubTakerGot; - - if (tesSUCCESS == terResult) - terResult = temUNCERTAIN; - } + if (tesSUCCESS == terResult) + terResult = temUNCERTAIN; } } } diff --git a/src/cpp/ripple/OfferCreateTransactor.h b/src/cpp/ripple/OfferCreateTransactor.h index 0947c656fd..dfe8696bca 100644 --- a/src/cpp/ripple/OfferCreateTransactor.h +++ b/src/cpp/ripple/OfferCreateTransactor.h @@ -6,6 +6,18 @@ class OfferCreateTransactor : public Transactor { protected: + bool bValidOffer( + SLE::ref sleOfferDir, + const uint256& uOffer, + const uint160& uOfferOwnerID, + const STAmount& saOfferPays, + const STAmount& saOfferGets, + const uint160& uTakerAccountID, + boost::unordered_set& usOfferUnfundedFound, + boost::unordered_set& usOfferUnfundedBecame, + boost::unordered_set& usAccountTouched, + STAmount& saOfferFunds); + TER takeOffers( const bool bOpenLedger, const bool bPassive,