diff --git a/src/cpp/ripple/CallRPC.cpp b/src/cpp/ripple/CallRPC.cpp index 9f67475f74..e4a71d56fe 100644 --- a/src/cpp/ripple/CallRPC.cpp +++ b/src/cpp/ripple/CallRPC.cpp @@ -209,11 +209,11 @@ Json::Value RPCParser::parseAccountTransactions(const Json::Value& jvParams) return jvRequest; } -// book_offers [ [ [ []]]] +// book_offers [ [ [ [ []]]]] // limit: 0 = no limit // proof: 0 or 1 // -// Mnemonic: taker puts --> offer --> taker gets +// Mnemonic: taker pays --> offer --> taker gets Json::Value RPCParser::parseBookOffers(const Json::Value& jvParams) { Json::Value jvRequest(Json::objectValue); @@ -239,24 +239,29 @@ Json::Value RPCParser::parseBookOffers(const Json::Value& jvParams) jvRequest["taker_gets"] = jvTakerGets; } - if (jvParams.size() >= 3 && !jvParseLedger(jvRequest, jvParams[2u].asString())) + if (jvParams.size() >= 3) + { + jvRequest["issuer"] = jvParams[2u].asString(); + } + + if (jvParams.size() >= 4 && !jvParseLedger(jvRequest, jvParams[3u].asString())) return jvRequest; - if (jvParams.size() >= 4) + if (jvParams.size() >= 5) { - int iLimit = jvParams[3u].asInt(); + int iLimit = jvParams[5u].asInt(); if (iLimit > 0) jvRequest["limit"] = iLimit; } - if (jvParams.size() >= 5 && jvParams[4u].asInt()) + if (jvParams.size() >= 6 && jvParams[5u].asInt()) { jvRequest["proof"] = true; } - if (jvParams.size() == 6) - jvRequest["marker"] = jvParams[5u]; + if (jvParams.size() == 7) + jvRequest["marker"] = jvParams[6u]; return jvRequest; } @@ -629,7 +634,7 @@ Json::Value RPCParser::parseCommand(std::string strMethod, Json::Value jvParams) { "account_lines", &RPCParser::parseAccountItems, 1, 2 }, { "account_offers", &RPCParser::parseAccountItems, 1, 2 }, { "account_tx", &RPCParser::parseAccountTransactions, 2, 4 }, - { "book_offers", &RPCParser::parseBookOffers, 2, 6 }, + { "book_offers", &RPCParser::parseBookOffers, 2, 7 }, { "connect", &RPCParser::parseConnect, 1, 2 }, { "consensus_info", &RPCParser::parseAsIs, 0, 0 }, { "get_counts", &RPCParser::parseGetCounts, 0, 1 }, diff --git a/src/cpp/ripple/NetworkOPs.cpp b/src/cpp/ripple/NetworkOPs.cpp index dcd526c842..61b2ded308 100644 --- a/src/cpp/ripple/NetworkOPs.cpp +++ b/src/cpp/ripple/NetworkOPs.cpp @@ -1782,13 +1782,17 @@ InfoSub::pointer NetworkOPs::addRpcSub(const std::string& strUrl, InfoSub::ref r return rspEntry; } -void NetworkOPs::getBookPage(Ledger::pointer lpLedger, const uint160& uTakerPaysCurrencyID, const uint160& uTakerPaysIssuerID, const uint160& uTakerGetsCurrencyID, const uint160& uTakerGetsIssuerID, const bool bProof, const unsigned int iLimit, const Json::Value& jvMarker, Json::Value& jvResult) +// FIXME : support iLimit. +void NetworkOPs::getBookPage(Ledger::pointer lpLedger, const uint160& uTakerPaysCurrencyID, const uint160& uTakerPaysIssuerID, const uint160& uTakerGetsCurrencyID, const uint160& uTakerGetsIssuerID, const uint160& uTakerID, const bool bProof, const unsigned int iLimit, const Json::Value& jvMarker, Json::Value& jvResult) { + boost::unordered_map umBalance; Json::Value jvOffers = Json::Value(Json::arrayValue); - const uint256 uBookBase = Ledger::getBookBase(uTakerGetsCurrencyID, uTakerGetsIssuerID, uTakerPaysCurrencyID, uTakerPaysIssuerID); + const uint256 uBookBase = Ledger::getBookBase(uTakerPaysCurrencyID, uTakerPaysIssuerID, uTakerGetsCurrencyID, uTakerGetsIssuerID); const uint256 uBookEnd = Ledger::getQualityNext(uBookBase); uint256 uTipIndex = uBookBase; + cLog(lsTRACE) << boost::str(boost::format("getBookPage: uTakerPaysCurrencyID=%s uTakerPaysIssuerID=%s") % STAmount::createHumanCurrency(uTakerPaysCurrencyID) % RippleAddress::createHumanAccountID(uTakerPaysIssuerID)); + cLog(lsTRACE) << boost::str(boost::format("getBookPage: uTakerGetsCurrencyID=%s uTakerGetsIssuerID=%s") % STAmount::createHumanCurrency(uTakerGetsCurrencyID) % RippleAddress::createHumanAccountID(uTakerGetsIssuerID)); cLog(lsTRACE) << boost::str(boost::format("getBookPage: uBookBase=%s") % uBookBase); cLog(lsTRACE) << boost::str(boost::format("getBookPage: uBookEnd=%s") % uBookEnd); cLog(lsTRACE) << boost::str(boost::format("getBookPage: uTipIndex=%s") % uTipIndex); @@ -1801,6 +1805,9 @@ void NetworkOPs::getBookPage(Ledger::pointer lpLedger, const uint160& uTakerPays SLE::pointer sleOfferDir; uint256 uOfferIndex; unsigned int uBookEntry; + STAmount saDirRate; + +// unsigned int iLeft = iLimit; while (!bDone) { if (bDirectAdvance) { @@ -1817,7 +1824,7 @@ void NetworkOPs::getBookPage(Ledger::pointer lpLedger, const uint160& uTakerPays else { uTipIndex = sleOfferDir->getIndex(); - + saDirRate = STAmount::setRate(Ledger::getQuality(uTipIndex)); SLE::pointer sleBookNode; lesActive.dirFirst(uTipIndex, sleBookNode, uBookEntry, uOfferIndex); @@ -1831,9 +1838,64 @@ void NetworkOPs::getBookPage(Ledger::pointer lpLedger, const uint160& uTakerPays { SLE::pointer sleOffer = lesActive.entryCache(ltOFFER, uOfferIndex); const uint160 uOfferOwnerID = sleOffer->getFieldAccount(sfAccount).getAccountID(); - STAmount saOfferPays = sleOffer->getFieldAmount(sfTakerGets); - STAmount saOfferGets = sleOffer->getFieldAmount(sfTakerPays); - STAmount saOfferFunds = lesActive.accountFunds(uOfferOwnerID, saOfferPays); + STAmount saOwnerFunds; + + boost::unordered_map::const_iterator umBalanceEntry = umBalance.find(uOfferOwnerID); + + if (umBalanceEntry == umBalance.end()) + { + // Did not find balance in table. + STAmount saDefault(uTakerGetsCurrencyID, uTakerGetsIssuerID); + // cLog(lsINFO) << boost::str(boost::format("getBookPage: saDefault=%s") % saDefault.getFullText()); + + saOwnerFunds = lesActive.accountFunds(uOfferOwnerID, saDefault); + // cLog(lsINFO) << boost::str(boost::format("getBookPage: saOwnerFunds=%s (new)") % saOwnerFunds.getFullText()); + } + else + { + saOwnerFunds = umBalanceEntry->second; + // cLog(lsINFO) << boost::str(boost::format("getBookPage: saOwnerFunds=%s (cached)") % saOwnerFunds.getFullText()); + } + + STAmount saTakerGets = sleOffer->getFieldAmount(sfTakerGets); + STAmount saTakerPays = sleOffer->getFieldAmount(sfTakerPays); + + Json::Value jvOffer = sleOffer->getJson(0); + + STAmount saTakerGetsFunded; + + if (saOwnerFunds >= saTakerGets) + { + // Sufficient funds no shenanigans. + saTakerGetsFunded = saTakerGets; + } + 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; + saTakerPaysFunded = std::min(saTakerPays, STAmount::multiply(saTakerGetsFunded, saDirRate, saTakerPays)); + + // Only provide, if not fully funded. + jvOffer["taker_gets_funded"] = saTakerGetsFunded.getJson(0); + jvOffer["taker_pays_funded"] = saTakerPaysFunded.getJson(0); + } + + 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)) { @@ -1842,8 +1904,6 @@ void NetworkOPs::getBookPage(Ledger::pointer lpLedger, const uint160& uTakerPays else { cLog(lsTRACE) << boost::str(boost::format("getBookPage: uOfferIndex=%s") % uOfferIndex); - - jvOffers.append(sleOffer->getJson(0)); } } } diff --git a/src/cpp/ripple/NetworkOPs.h b/src/cpp/ripple/NetworkOPs.h index 1795b41ead..188543d7c6 100644 --- a/src/cpp/ripple/NetworkOPs.h +++ b/src/cpp/ripple/NetworkOPs.h @@ -233,7 +233,7 @@ public: // Book functions // - void getBookPage(Ledger::pointer lpLedger, const uint160& uTakerPaysCurrencyID, const uint160& uTakerPaysIssuerID, const uint160& uTakerGetsCurrencyID, const uint160& uTakerGetsIssuerID, const bool bProof, const unsigned int iLimit, const Json::Value& jvMarker, Json::Value& jvResult); + void getBookPage(Ledger::pointer lpLedger, const uint160& uTakerPaysCurrencyID, const uint160& uTakerPaysIssuerID, const uint160& uTakerGetsCurrencyID, const uint160& uTakerGetsIssuerID, const uint160& uTakerID, const bool bProof, const unsigned int iLimit, const Json::Value& jvMarker, Json::Value& jvResult); // raw object operations bool findRawLedger(const uint256& ledgerHash, std::vector& rawLedger); diff --git a/src/cpp/ripple/RPCErr.cpp b/src/cpp/ripple/RPCErr.cpp index bf8c7103eb..d7307cb776 100644 --- a/src/cpp/ripple/RPCErr.cpp +++ b/src/cpp/ripple/RPCErr.cpp @@ -19,7 +19,9 @@ Json::Value rpcError(int iError, Json::Value jvResult) { rpcACT_MALFORMED, "actMalformed", "Account malformed." }, { rpcACT_NOT_FOUND, "actNotFound", "Account not found." }, { rpcBAD_BLOB, "badBlob", "Blob must be a non-empty hex string." }, + { rpcBAD_ISSUER, "badIssuer", "Issuer account malformed." }, { rpcBAD_MARKET, "badMarket", "No such market." }, + { rpcBAD_SECRET, "badSecret", "Secret does not match account." }, { rpcBAD_SEED, "badSeed", "Disallowed seed." }, { rpcBAD_SYNTAX, "badSyntax", "Syntax error." }, { rpcCOMMAND_MISSING, "commandMissing", "Missing command entry." }, @@ -62,7 +64,6 @@ Json::Value rpcError(int iError, Json::Value jvResult) { rpcSRC_ACT_MALFORMED, "srcActMalformed", "Source account is malformed." }, { rpcSRC_ACT_MISSING, "srcActMissing", "Source account not provided." }, { rpcSRC_ACT_NOT_FOUND, "srcActNotFound", "Source account not found." }, - { rpcBAD_SECRET, "badSecret", "Secret does not match account." }, { rpcSRC_AMT_MALFORMED, "srcAmtMalformed", "Source amount/currency/issuer is malformed." }, { rpcSRC_CUR_MALFORMED, "srcCurMalformed", "Source currency is malformed." }, { rpcSRC_ISR_MALFORMED, "srcIsrMalformed", "Source issuer is malformed." }, diff --git a/src/cpp/ripple/RPCErr.h b/src/cpp/ripple/RPCErr.h index e34fef9228..ef27251cb9 100644 --- a/src/cpp/ripple/RPCErr.h +++ b/src/cpp/ripple/RPCErr.h @@ -45,7 +45,9 @@ enum { rpcACT_MALFORMED, rpcQUALITY_MALFORMED, rpcBAD_BLOB, + rpcBAD_ISSUER, rpcBAD_MARKET, + rpcBAD_SECRET, rpcBAD_SEED, rpcCOMMAND_MISSING, rpcDST_ACT_MALFORMED, @@ -69,7 +71,6 @@ enum { rpcSRC_AMT_MALFORMED, rpcSRC_CUR_MALFORMED, rpcSRC_ISR_MALFORMED, - rpcBAD_SECRET, // Internal error (should never happen) rpcINTERNAL, // Generic internal error. diff --git a/src/cpp/ripple/RPCHandler.cpp b/src/cpp/ripple/RPCHandler.cpp index 5189c1515c..ff93bfd0e9 100644 --- a/src/cpp/ripple/RPCHandler.cpp +++ b/src/cpp/ripple/RPCHandler.cpp @@ -1022,10 +1022,11 @@ Json::Value RPCHandler::doAccountOffers(Json::Value jvRequest) } // { -// "ledger_hash" : ledger, // Optional -// "ledger_index" : ledger_index, // Optional +// "ledger_hash" : ledger, // Optional. +// "ledger_index" : ledger_index, // Optional. // "taker_gets" : { "currency": currency, "issuer" : address }, // "taker_pays" : { "currency": currency, "issuer" : address }, +// "taker" : address, // Optional. // "marker" : element, // Optional. // "limit" : integer, // Optional. // "proof" : boolean // Defaults to false. @@ -1100,11 +1101,22 @@ Json::Value RPCHandler::doBookOffers(Json::Value jvRequest) return rpcError(rpcBAD_MARKET); } + RippleAddress raTakerID; + + if (!jvRequest.isMember("taker")) + { + raTakerID.setAccountID(ACCOUNT_ONE); + } + else if (!raTakerID.setAccountID(jvRequest["taker"].asString())) + { + return rpcError(rpcBAD_ISSUER); + } + const bool bProof = jvRequest.isMember("proof"); const unsigned int iLimit = jvRequest.isMember("limit") ? jvRequest["limit"].asUInt() : 0; const Json::Value jvMarker = jvRequest.isMember("marker") ? jvRequest["marker"] : Json::Value(Json::nullValue); - mNetOps->getBookPage(lpLedger, uTakerPaysCurrencyID, uTakerPaysIssuerID, uTakerGetsCurrencyID, uTakerGetsIssuerID, bProof, iLimit, jvMarker, jvResult); + mNetOps->getBookPage(lpLedger, uTakerPaysCurrencyID, uTakerPaysIssuerID, uTakerGetsCurrencyID, uTakerGetsIssuerID, raTakerID.getAccountID(), bProof, iLimit, jvMarker, jvResult); return jvResult; } diff --git a/src/cpp/ripple/main.cpp b/src/cpp/ripple/main.cpp index c3ec7a9a22..15783f8afb 100644 --- a/src/cpp/ripple/main.cpp +++ b/src/cpp/ripple/main.cpp @@ -74,7 +74,7 @@ void printHelp(const po::options_description& desc) cerr << " account_lines || []" << endl; cerr << " account_offers || []" << endl; cerr << " account_tx || |( )" << endl; - cerr << " book_offers [ [ [ []]]]" << endl; + cerr << " book_offers [ [ [ []]]]]" << endl; cerr << " connect []" << endl; cerr << " consensus_info" << endl; #if ENABLE_INSECURE