Abstract valid offer testing in OfferCreate.

This commit is contained in:
Arthur Britto
2013-03-21 16:27:24 -07:00
parent edd79c50a9
commit 4ac824419a
2 changed files with 183 additions and 138 deletions

View File

@@ -7,6 +7,80 @@
SETUP_LOG(); 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<uint256>& usOfferUnfundedFound,
boost::unordered_set<uint256>& usOfferUnfundedBecame,
boost::unordered_set<uint160>& 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<uint160>::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. // Take as much as possible. Adjusts account balances. Charges fees on top to taker.
// --> uBookBase: The order book to take against. // --> uBookBase: The order book to take against.
// --> saTakerPays: What the taker offers (w/ issuer) // --> saTakerPays: What the taker offers (w/ issuer)
@@ -28,7 +102,7 @@ TER OfferCreateTransactor::takeOffers(
bool& bUnfunded) bool& bUnfunded)
{ {
// The book has the most elements. Take the perspective of the book. // 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. // The order is for the other books currencys for get and pays are opposites.
// We want the same ratio for the respective currencies. // We want the same ratio for the respective currencies.
@@ -141,162 +215,121 @@ TER OfferCreateTransactor::takeOffers(
STAmount saOfferPays = sleOffer->getFieldAmount(sfTakerGets); STAmount saOfferPays = sleOffer->getFieldAmount(sfTakerGets);
STAmount saOfferGets = sleOffer->getFieldAmount(sfTakerPays); STAmount saOfferGets = sleOffer->getFieldAmount(sfTakerPays);
if (sleOffer->isFieldPresent(sfExpiration) && sleOffer->getFieldU32(sfExpiration) <= mEngine->getLedger()->getParentCloseTimeNC()) STAmount saOfferFunds; // Funds of offer owner to payout.
{ bool bValid;
// Offer is expired. Expired offers are considered unfunded. Delete it.
cLog(lsINFO) << "takeOffers: encountered expired offer";
usOfferUnfundedFound.insert(uOfferIndex); bValid = bValidOffer(
} sleOfferDir, uOfferIndex, uOfferOwnerID, saOfferPays, saOfferGets,
else if (uOfferOwnerID == uTakerAccountID) uTakerAccountID,
{ usOfferUnfundedFound, usOfferUnfundedBecame, usAccountTouched,
// Would take own offer. Consider old offer expired. Delete it. saOfferFunds);
cLog(lsINFO) << "takeOffers: encountered taker's own old offer";
usOfferUnfundedBecame.insert(uOfferIndex); if (bValid) {
} STAmount saSubTakerPaid;
else if (!saOfferGets.isPositive() || !saOfferPays.isPositive()) STAmount saSubTakerGot;
{ STAmount saTakerIssuerFee;
// Offer has bad amounts. Consider offer expired. Delete it. STAmount saOfferIssuerFee;
cLog(lsWARNING) << boost::str(boost::format("takeOffers: BAD OFFER: saOfferPays=%s saOfferGets=%s") STAmount saOfferRate = STAmount::setRate(uTipQuality);
% saOfferPays % saOfferGets);
usOfferUnfundedFound.insert(uOfferIndex); cLog(lsINFO) << "takeOffers: applyOffer: saTakerPays: " << saTakerPays.getFullText();
} cLog(lsINFO) << "takeOffers: applyOffer: saTakerPaid: " << saTakerPaid.getFullText();
else cLog(lsINFO) << "takeOffers: applyOffer: saTakerFunds: " << saTakerFunds.getFullText();
{ cLog(lsINFO) << "takeOffers: applyOffer: saOfferFunds: " << saOfferFunds.getFullText();
// Get offer funds available. 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); cLog(lsINFO) << "takeOffers: applyOffer: saSubTakerPaid: " << saSubTakerPaid.getFullText();
SLE::pointer sleOfferAccount; // Owner of offer. 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. // Offer now fully claimed or now unfunded.
cLog(lsINFO) << "takeOffers: offer unfunded: delete"; cLog(lsINFO) << "takeOffers: Offer claimed: Delete.";
boost::unordered_set<uint160>::iterator account = usAccountTouched.find(uOfferOwnerID); usOfferUnfundedBecame.insert(uOfferIndex); // Delete unfunded offer on success.
if (account != usAccountTouched.end())
// 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. cLog(lsWARNING) << "takeOffers: ILLEGAL OFFER RESULT.";
usOfferUnfundedBecame.insert(uOfferIndex); // Delete unfunded offer on success. bUnfunded = true;
} terResult = bOpenLedger ? telFAILED_PROCESSING : tecFAILED_PROCESSING;
else
{
// Never touched source account.
usOfferUnfundedFound.insert(uOfferIndex); // Delete found unfunded offer when possible.
} }
} }
else else
{ {
STAmount saSubTakerPaid; // Taker got nothing, probably due to rounding. Consider taker unfunded.
STAmount saSubTakerGot; cLog(lsINFO) << "takeOffers: No claim.";
STAmount saTakerIssuerFee;
STAmount saOfferIssuerFee;
STAmount saOfferRate = STAmount::setRate(uTipQuality);
cLog(lsINFO) << "takeOffers: applyOffer: saTakerPays: " << saTakerPays.getFullText(); bUnfunded = true;
cLog(lsINFO) << "takeOffers: applyOffer: saTakerPaid: " << saTakerPaid.getFullText(); terResult = tesSUCCESS; // Done.
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();
bool bOfferDelete = STAmount::applyOffer( assert(uTakerGetsAccountID == saSubTakerGot.getIssuer());
lesActive.rippleTransferRate(uTakerAccountID, uOfferOwnerID, uTakerPaysAccountID), assert(uTakerPaysAccountID == saSubTakerPaid.getIssuer());
lesActive.rippleTransferRate(uOfferOwnerID, uTakerAccountID, uTakerGetsAccountID),
saOfferRate,
saOfferFunds,
saTakerFunds,
saOfferPays,
saOfferGets,
saSubTakerPays,
saSubTakerGets,
saSubTakerPaid,
saSubTakerGot,
saTakerIssuerFee,
saOfferIssuerFee);
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: 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. if (tesSUCCESS == terResult)
sleOffer->setFieldAmount(sfTakerGets, saOfferPays -= saSubTakerGot); terResult = temUNCERTAIN;
// 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;
}
} }
} }
} }

View File

@@ -6,6 +6,18 @@
class OfferCreateTransactor : public Transactor class OfferCreateTransactor : public Transactor
{ {
protected: protected:
bool bValidOffer(
SLE::ref sleOfferDir,
const uint256& uOffer,
const uint160& uOfferOwnerID,
const STAmount& saOfferPays,
const STAmount& saOfferGets,
const uint160& uTakerAccountID,
boost::unordered_set<uint256>& usOfferUnfundedFound,
boost::unordered_set<uint256>& usOfferUnfundedBecame,
boost::unordered_set<uint160>& usAccountTouched,
STAmount& saOfferFunds);
TER takeOffers( TER takeOffers(
const bool bOpenLedger, const bool bOpenLedger,
const bool bPassive, const bool bPassive,