Handle offers in quality directory but not in ledger

This commit is contained in:
JoelKatz
2014-01-02 03:35:34 -08:00
committed by Vinnie Falco
parent 9abdd16721
commit 8f8b2ae4a3
3 changed files with 340 additions and 316 deletions

View File

@@ -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<uint160, STAmount>::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<uint160, STAmount>::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))
{

View File

@@ -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));

View File

@@ -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
}
}
}