Add support for transfer fees to RPC book_offers.

This commit is contained in:
Arthur Britto
2013-03-03 21:31:34 -08:00
parent da184a97c4
commit 49458bff57
2 changed files with 111 additions and 93 deletions

View File

@@ -1066,6 +1066,8 @@ STAmount LedgerEntrySet::rippleHolds(const uint160& uAccountID, const uint160& u
return saBalance; return saBalance;
} }
// Returns the amount an account can spend without going into debt.
//
// <-- saAmount: amount of uCurrencyID held by uAccountID. May be negative. // <-- saAmount: amount of uCurrencyID held by uAccountID. May be negative.
STAmount LedgerEntrySet::accountHolds(const uint160& uAccountID, const uint160& uCurrencyID, const uint160& uIssuerID) STAmount LedgerEntrySet::accountHolds(const uint160& uAccountID, const uint160& uCurrencyID, const uint160& uIssuerID)
{ {

View File

@@ -1784,121 +1784,137 @@ void NetworkOPs::getBookPage(Ledger::pointer lpLedger, const uint160& uTakerPays
STAmount saDirRate; STAmount saDirRate;
// unsigned int iLeft = iLimit; // unsigned int iLeft = iLimit;
SLE::pointer sleGetsIssuer = lesActive.entryCache(ltACCOUNT_ROOT, Ledger::getAccountRootIndex(uTakerGetsIssuerID));
if (!sleGetsIssuer) uint32 uTransferRate = lesActive.rippleTransferRate(uTakerGetsIssuerID);
{
// Order book can not exist if TakerGetsIssuer account does not exist.
nothing(); while (!bDone) {
} if (bDirectAdvance) {
else bDirectAdvance = false;
{
while (!bDone) {
if (bDirectAdvance) {
bDirectAdvance = false;
cLog(lsTRACE) << boost::str(boost::format("getBookPage: bDirectAdvance")); cLog(lsTRACE) << boost::str(boost::format("getBookPage: bDirectAdvance"));
sleOfferDir = lesActive.entryCache(ltDIR_NODE, lpLedger->getNextLedgerIndex(uTipIndex, uBookEnd)); sleOfferDir = lesActive.entryCache(ltDIR_NODE, lpLedger->getNextLedgerIndex(uTipIndex, uBookEnd));
if (!sleOfferDir) if (!sleOfferDir)
{
cLog(lsTRACE) << boost::str(boost::format("getBookPage: bDone"));
bDone = true;
}
else
{
uTipIndex = sleOfferDir->getIndex();
saDirRate = STAmount::setRate(Ledger::getQuality(uTipIndex));
SLE::pointer sleBookNode;
lesActive.dirFirst(uTipIndex, sleBookNode, uBookEntry, uOfferIndex);
cLog(lsTRACE) << boost::str(boost::format("getBookPage: uTipIndex=%s") % uTipIndex);
cLog(lsTRACE) << boost::str(boost::format("getBookPage: uOfferIndex=%s") % uOfferIndex);
}
}
if (!bDone)
{
SLE::pointer sleOffer = lesActive.entryCache(ltOFFER, uOfferIndex);
const uint160 uOfferOwnerID = sleOffer->getFieldAccount(sfAccount).getAccountID();
STAmount saTakerGets = sleOffer->getFieldAmount(sfTakerGets);
STAmount saTakerPays = sleOffer->getFieldAmount(sfTakerPays);
STAmount saOwnerFunds;
if (uTakerGetsIssuerID == uOfferOwnerID)
{
// If offer is selling issuer's own IOUs, it is fully funded.
saOwnerFunds = saTakerGets;
}
else
{
boost::unordered_map<uint160, STAmount>::const_iterator umBalanceEntry = umBalance.find(uOfferOwnerID);
if (umBalanceEntry != umBalance.end())
{ {
cLog(lsTRACE) << boost::str(boost::format("getBookPage: bDone")); // Found in running balance table.
bDone = true;
saOwnerFunds = umBalanceEntry->second;
// cLog(lsINFO) << boost::str(boost::format("getBookPage: saOwnerFunds=%s (cached)") % saOwnerFunds.getFullText());
} }
else else
{ {
uTipIndex = sleOfferDir->getIndex(); // Did not find balance in table.
saDirRate = STAmount::setRate(Ledger::getQuality(uTipIndex));
SLE::pointer sleBookNode;
lesActive.dirFirst(uTipIndex, sleBookNode, uBookEntry, uOfferIndex); saOwnerFunds = lesActive.accountHolds(uOfferOwnerID, uTakerGetsCurrencyID, uTakerGetsIssuerID);
// cLog(lsINFO) << boost::str(boost::format("getBookPage: saOwnerFunds=%s (new)") % saOwnerFunds.getFullText());
if (saOwnerFunds.isNegative())
{
// Treat negative funds as zero.
cLog(lsTRACE) << boost::str(boost::format("getBookPage: uTipIndex=%s") % uTipIndex); saOwnerFunds.zero();
cLog(lsTRACE) << boost::str(boost::format("getBookPage: uOfferIndex=%s") % uOfferIndex); }
} }
} }
if (!bDone) Json::Value jvOffer = sleOffer->getJson(0);
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));
}
else
{ {
SLE::pointer sleOffer = lesActive.entryCache(ltOFFER, uOfferIndex); uOfferRate = QUALITY_ONE;
const uint160 uOfferOwnerID = sleOffer->getFieldAccount(sfAccount).getAccountID(); saOwnerFundsLimit = saOwnerFunds;
STAmount saTakerGets = sleOffer->getFieldAmount(sfTakerGets); }
STAmount saTakerPays = sleOffer->getFieldAmount(sfTakerPays);
STAmount saOwnerFunds;
if (uTakerGetsIssuerID == uOfferOwnerID) if (saOwnerFundsLimit >= saTakerGets)
{ {
// If offer is selling issuer's own IOUs, it is fully funded. // Sufficient funds no shenanigans.
saOwnerFunds = saTakerGets; saTakerGetsFunded = saTakerGets;
} }
else else
{ {
boost::unordered_map<uint160, STAmount>::const_iterator umBalanceEntry = umBalance.find(uOfferOwnerID); // cLog(lsINFO) << boost::str(boost::format("getBookPage: saTakerGets=%s") % saTakerGets.getFullText());
// cLog(lsINFO) << boost::str(boost::format("getBookPage: saTakerPays=%s") % saTakerPays.getFullText());
// cLog(lsINFO) << boost::str(boost::format("getBookPage: saOwnerFunds=%s") % saOwnerFunds.getFullText());
// cLog(lsINFO) << boost::str(boost::format("getBookPage: saDirRate=%s") % saDirRate.getText());
// cLog(lsINFO) << boost::str(boost::format("getBookPage: multiply=%s") % STAmount::multiply(saTakerGetsFunded, saDirRate).getFullText());
// cLog(lsINFO) << boost::str(boost::format("getBookPage: multiply=%s") % STAmount::multiply(saTakerGetsFunded, saDirRate, saTakerPays).getFullText());
STAmount saTakerPaysFunded;
if (umBalanceEntry != umBalance.end()) saTakerGetsFunded = saOwnerFundsLimit;
{ saTakerPaysFunded = std::min(saTakerPays, STAmount::multiply(saTakerGetsFunded, saDirRate, saTakerPays));
// Found in running balance table.
saOwnerFunds = umBalanceEntry->second; // Only provide, if not fully funded.
// cLog(lsINFO) << boost::str(boost::format("getBookPage: saOwnerFunds=%s (cached)") % saOwnerFunds.getFullText()); jvOffer["taker_gets_funded"] = saTakerGetsFunded.getJson(0);
} jvOffer["taker_pays_funded"] = saTakerPaysFunded.getJson(0);
else
{
// Did not find balance in table.
saOwnerFunds = lesActive.accountHolds(uOfferOwnerID, uTakerGetsCurrencyID, uTakerGetsIssuerID); }
// cLog(lsINFO) << boost::str(boost::format("getBookPage: saOwnerFunds=%s (new)") % saOwnerFunds.getFullText()); STAmount saOwnerPays = QUALITY_ONE == uOfferRate
if (saOwnerFunds.isNegative()) ? saTakerGetsFunded
saOwnerFunds.zero(); : std::min(saOwnerFunds, STAmount::multiply(saTakerGetsFunded, STAmount(CURRENCY_ONE, ACCOUNT_ONE, uOfferRate, -9)));
}
}
Json::Value jvOffer = sleOffer->getJson(0); STAmount saOwnerBalance = saOwnerFunds-saOwnerPays;
STAmount saTakerGetsFunded; umBalance[uOfferOwnerID] = saOwnerBalance;
if (saOwnerFunds >= saTakerGets) if (!saOwnerFunds.isZero() || uOfferOwnerID == uTakerID)
{ {
// Sufficient funds no shenanigans. // Only provide funded offers and offers of the taker.
saTakerGetsFunded = saTakerGets; jvOffers.append(jvOffer);
} }
else
{
// cLog(lsINFO) << boost::str(boost::format("getBookPage: saTakerGets=%s") % saTakerGets.getFullText());
// cLog(lsINFO) << boost::str(boost::format("getBookPage: saTakerPays=%s") % saTakerPays.getFullText());
// cLog(lsINFO) << boost::str(boost::format("getBookPage: saOwnerFunds=%s") % saOwnerFunds.getFullText());
// cLog(lsINFO) << boost::str(boost::format("getBookPage: saDirRate=%s") % saDirRate.getText());
// cLog(lsINFO) << boost::str(boost::format("getBookPage: multiply=%s") % STAmount::multiply(saTakerGetsFunded, saDirRate).getFullText());
// cLog(lsINFO) << boost::str(boost::format("getBookPage: multiply=%s") % STAmount::multiply(saTakerGetsFunded, saDirRate, saTakerPays).getFullText());
STAmount saTakerPaysFunded;
saTakerGetsFunded = saOwnerFunds; if (!lesActive.dirNext(uTipIndex, sleOfferDir, uBookEntry, uOfferIndex))
saTakerPaysFunded = std::min(saTakerPays, STAmount::multiply(saTakerGetsFunded, saDirRate, saTakerPays)); {
bDirectAdvance = true;
// Only provide, if not fully funded. }
jvOffer["taker_gets_funded"] = saTakerGetsFunded.getJson(0); else
jvOffer["taker_pays_funded"] = saTakerPaysFunded.getJson(0); {
} cLog(lsTRACE) << boost::str(boost::format("getBookPage: uOfferIndex=%s") % uOfferIndex);
STAmount saOwnerBalance = saOwnerFunds-saTakerGetsFunded;
umBalance[uOfferOwnerID] = saOwnerBalance;
if (!saOwnerFunds.isZero() || uOfferOwnerID == uTakerID)
{
// Only provide funded offers and offers of the taker.
jvOffers.append(jvOffer);
}
if (!lesActive.dirNext(uTipIndex, sleOfferDir, uBookEntry, uOfferIndex))
{
bDirectAdvance = true;
}
else
{
cLog(lsTRACE) << boost::str(boost::format("getBookPage: uOfferIndex=%s") % uOfferIndex);
}
} }
} }
} }