diff --git a/ripple2010.vcxproj b/ripple2010.vcxproj index dbb3dd84b..085035df9 100644 --- a/ripple2010.vcxproj +++ b/ripple2010.vcxproj @@ -109,6 +109,7 @@ + @@ -295,8 +296,9 @@ Designer - + + Document @@ -307,7 +309,7 @@ - + diff --git a/src/cpp/ripple/Application.cpp b/src/cpp/ripple/Application.cpp index 4c07f2b2e..f0c360265 100644 --- a/src/cpp/ripple/Application.cpp +++ b/src/cpp/ripple/Application.cpp @@ -26,7 +26,9 @@ Application* theApp = NULL; DatabaseCon::DatabaseCon(const std::string& strName, const char *initStrings[], int initCount) { - boost::filesystem::path pPath = theConfig.RUN_STANDALONE ? "" : theConfig.DATA_DIR / strName; + boost::filesystem::path pPath = (theConfig.RUN_STANDALONE && (!theConfig.START_UP != Config::LOAD)) + ? "" // Use temporary files. + : (theConfig.DATA_DIR / strName); // Use regular db files. mDatabase = new SqliteDatabase(pPath.string().c_str()); mDatabase->connect(); @@ -43,7 +45,7 @@ DatabaseCon::~DatabaseCon() Application::Application() : mIOWork(mIOService), mAuxWork(mAuxService), mUNL(mIOService), mNetOps(mIOService, &mLedgerMaster), mTempNodeCache("NodeCache", 16384, 90), mHashedObjectStore(16384, 300), mSLECache("LedgerEntryCache", 4096, 120), - mSNTPClient(mAuxService), mRPCHandler(&mNetOps), mFeeTrack(), + mSNTPClient(mAuxService), mFeeTrack(), mRpcDB(NULL), mTxnDB(NULL), mLedgerDB(NULL), mWalletDB(NULL), mHashNodeDB(NULL), mNetNodeDB(NULL), mConnectionPool(mIOService), mPeerDoor(NULL), mRPCDoor(NULL), mWSPublicDoor(NULL), mWSPrivateDoor(NULL), mSweepTimer(mAuxService), mShutdown(false) diff --git a/src/cpp/ripple/Application.h b/src/cpp/ripple/Application.h index cafa5b7c7..48e58d97a 100644 --- a/src/cpp/ripple/Application.h +++ b/src/cpp/ripple/Application.h @@ -64,7 +64,6 @@ class Application SLECache mSLECache; SNTPClient mSNTPClient; JobQueue mJobQueue; - RPCHandler mRPCHandler; ProofOfWorkGenerator mPOWGen; LoadManager mLoadMgr; LoadFeeTrack mFeeTrack; @@ -114,7 +113,6 @@ public: ValidationCollection& getValidations() { return mValidations; } JobQueue& getJobQueue() { return mJobQueue; } SuppressionTable& getSuppression() { return mSuppressions; } - RPCHandler& getRPCHandler() { return mRPCHandler; } boost::recursive_mutex& getMasterLock() { return mMasterLock; } ProofOfWorkGenerator& getPowGen() { return mPOWGen; } LoadManager& getLoadManager() { return mLoadMgr; } diff --git a/src/cpp/ripple/CallRPC.cpp b/src/cpp/ripple/CallRPC.cpp index e4a71d56f..4afb21a3c 100644 --- a/src/cpp/ripple/CallRPC.cpp +++ b/src/cpp/ripple/CallRPC.cpp @@ -442,16 +442,23 @@ Json::Value RPCParser::parseAccountItems(const Json::Value& jvParams) return jvRequest; } -// ripple_path_find json +// ripple_path_find [] Json::Value RPCParser::parseRipplePathFind(const Json::Value& jvParams) { - Json::Value txJSON; Json::Reader reader; + Json::Value jvRequest; + bool bLedger = 2 == jvParams.size(); - cLog(lsTRACE) << "RPC json:" << jvParams[0u]; - if (reader.parse(jvParams[0u].asString(), txJSON)) + cLog(lsTRACE) << "RPC json: " << jvParams[0u]; + + if (bLedger) { - return txJSON; + jvParseLedger(jvRequest, jvParams[1u].asString()); + } + + if (reader.parse(jvParams[0u].asString(), jvRequest)) + { + return jvRequest; } return rpcError(rpcINVALID_PARAMS); @@ -652,7 +659,7 @@ Json::Value RPCParser::parseCommand(std::string strMethod, Json::Value jvParams) { "ping", &RPCParser::parseAsIs, 0, 0 }, // { "profile", &RPCParser::parseProfile, 1, 9 }, { "random", &RPCParser::parseAsIs, 0, 0 }, - { "ripple_path_find", &RPCParser::parseRipplePathFind, 1, 1 }, + { "ripple_path_find", &RPCParser::parseRipplePathFind, 1, 2 }, { "sign", &RPCParser::parseSignSubmit, 2, 2 }, { "submit", &RPCParser::parseSignSubmit, 1, 2 }, { "server_info", &RPCParser::parseAsIs, 0, 0 }, diff --git a/src/cpp/ripple/LoadManager.h b/src/cpp/ripple/LoadManager.h index 55796ea02..8899bf575 100644 --- a/src/cpp/ripple/LoadManager.h +++ b/src/cpp/ripple/LoadManager.h @@ -61,14 +61,20 @@ public: static const int lsfOutbound = 2; // outbound connection protected: - int mBalance; - int mFlags; - int mLastUpdate; - int mLastWarning; + std::string mName; + int mBalance; + int mFlags; + int mLastUpdate; + int mLastWarning; public: - LoadSource() : mBalance(0), mFlags(0), mLastWarning(0) - { mLastUpdate = upTime(); } + LoadSource(bool admin) : mBalance(0), mFlags(admin ? lsfPrivileged : 0), mLastUpdate(upTime()), mLastWarning(0) + { ; } + LoadSource(const std::string& name) : mName(name), mBalance(0), mFlags(0), mLastUpdate(upTime()), mLastWarning(0) + { ; } + + void rename(const std::string& name) + { mName = name; } bool isPrivileged() const { return (mFlags & lsfPrivileged) != 0; } void setPrivileged() { mFlags |= lsfPrivileged; } diff --git a/src/cpp/ripple/OfferCreateTransactor.cpp b/src/cpp/ripple/OfferCreateTransactor.cpp index 5055e8715..f3251c065 100644 --- a/src/cpp/ripple/OfferCreateTransactor.cpp +++ b/src/cpp/ripple/OfferCreateTransactor.cpp @@ -38,6 +38,7 @@ TER OfferCreateTransactor::takeOffers( cLog(lsINFO) << "takeOffers: against book: " << uBookBase.ToString(); + LedgerEntrySet& lesActive = mEngine->getNodes(); uint256 uTipIndex = uBookBase; const uint256 uBookEnd = Ledger::getQualityNext(uBookBase); const uint64 uTakeQuality = STAmount::getRate(saTakerGets, saTakerPays); @@ -46,7 +47,6 @@ TER OfferCreateTransactor::takeOffers( const uint160 uTakerGetsAccountID = saTakerGets.getIssuer(); TER terResult = temUNCERTAIN; - boost::unordered_set usOfferUnfundedFound; // Offers found unfunded. boost::unordered_set usOfferUnfundedBecame; // Offers that became unfunded. boost::unordered_set usAccountTouched; // Accounts touched. @@ -58,7 +58,7 @@ TER OfferCreateTransactor::takeOffers( { SLE::pointer sleOfferDir; uint64 uTipQuality = 0; - STAmount saTakerFunds = mEngine->getNodes().accountFunds(uTakerAccountID, saTakerPays); + STAmount saTakerFunds = lesActive.accountFunds(uTakerAccountID, saTakerPays); STAmount saSubTakerPays = saTakerPays-saTakerPaid; // How much more to spend. STAmount saSubTakerGets = saTakerGets-saTakerGot; // How much more is wanted. @@ -131,7 +131,7 @@ TER OfferCreateTransactor::takeOffers( unsigned int uBookEntry; uint256 uOfferIndex; - mEngine->getNodes().dirFirst(uTipIndex, sleBookNode, uBookEntry, uOfferIndex); + lesActive.dirFirst(uTipIndex, sleBookNode, uBookEntry, uOfferIndex); SLE::pointer sleOffer = mEngine->entryCache(ltOFFER, uOfferIndex); @@ -153,7 +153,7 @@ TER OfferCreateTransactor::takeOffers( // Would take own offer. Consider old offer expired. Delete it. cLog(lsINFO) << "takeOffers: encountered taker's own old offer"; - usOfferUnfundedFound.insert(uOfferIndex); + usOfferUnfundedBecame.insert(uOfferIndex); } else if (!saOfferGets.isPositive() || !saOfferPays.isPositive()) { @@ -169,7 +169,7 @@ TER OfferCreateTransactor::takeOffers( cLog(lsINFO) << "takeOffers: saOfferPays=" << saOfferPays.getFullText(); - STAmount saOfferFunds = mEngine->getNodes().accountFunds(uOfferOwnerID, saOfferPays); + STAmount saOfferFunds = lesActive.accountFunds(uOfferOwnerID, saOfferPays); SLE::pointer sleOfferAccount; // Owner of offer. if (!saOfferFunds.isPositive()) // Includes zero. @@ -210,8 +210,8 @@ TER OfferCreateTransactor::takeOffers( cLog(lsINFO) << "takeOffers: applyOffer: saTakerGets: " << saTakerGets.getFullText(); bool bOfferDelete = STAmount::applyOffer( - mEngine->getNodes().rippleTransferRate(uTakerAccountID, uOfferOwnerID, uTakerPaysAccountID), - mEngine->getNodes().rippleTransferRate(uOfferOwnerID, uTakerAccountID, uTakerGetsAccountID), + lesActive.rippleTransferRate(uTakerAccountID, uOfferOwnerID, uTakerPaysAccountID), + lesActive.rippleTransferRate(uOfferOwnerID, uTakerAccountID, uTakerGetsAccountID), saOfferRate, saOfferFunds, saTakerFunds, @@ -274,10 +274,10 @@ TER OfferCreateTransactor::takeOffers( { // Distribute funds. The sends charge appropriate fees which are implied by offer. - terResult = mEngine->getNodes().accountSend(uOfferOwnerID, uTakerAccountID, saSubTakerGot); // Offer owner pays taker. + terResult = lesActive.accountSend(uOfferOwnerID, uTakerAccountID, saSubTakerGot); // Offer owner pays taker. if (tesSUCCESS == terResult) - terResult = mEngine->getNodes().accountSend(uTakerAccountID, uOfferOwnerID, saSubTakerPaid); // Taker pays offer owner. + 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. @@ -304,20 +304,6 @@ TER OfferCreateTransactor::takeOffers( cLog(lsINFO) << "takeOffers: " << transToken(terResult); - // On storing meta data, delete offers that were found unfunded to prevent encountering them in future. - if (tesSUCCESS == terResult) - { - BOOST_FOREACH(const uint256& uOfferIndex, usOfferUnfundedFound) - { - - cLog(lsINFO) << "takeOffers: found unfunded: " << uOfferIndex.ToString(); - - terResult = mEngine->getNodes().offerDelete(uOfferIndex); - if (tesSUCCESS != terResult) - break; - } - } - if (tesSUCCESS == terResult) { // On success, delete offers that became unfunded. @@ -325,7 +311,7 @@ TER OfferCreateTransactor::takeOffers( { cLog(lsINFO) << "takeOffers: became unfunded: " << uOfferIndex.ToString(); - terResult = mEngine->getNodes().offerDelete(uOfferIndex); + terResult = lesActive.offerDelete(uOfferIndex); if (tesSUCCESS != terResult) break; } @@ -369,6 +355,12 @@ TER OfferCreateTransactor::doApply() uint64 uOwnerNode; uint64 uBookNode; + LedgerEntrySet& lesActive = mEngine->getNodes(); + LedgerEntrySet lesCheckpoint = lesActive; // Checkpoint with just fees paid. + lesActive.bumpSeq(); // Begin ledger variance. + + SLE::pointer sleCreator = mEngine->entryCache(ltACCOUNT_ROOT, Ledger::getAccountRootIndex(mTxnAccountID)); + if (uTxFlags & tfOfferCreateMask) { cLog(lsINFO) << "OfferCreate: Malformed transaction: Invalid flags set."; @@ -417,7 +409,7 @@ TER OfferCreateTransactor::doApply() terResult = temBAD_ISSUER; } - else if (!mEngine->getNodes().accountFunds(mTxnAccountID, saTakerGets).isPositive()) + else if (!lesActive.accountFunds(mTxnAccountID, saTakerGets).isPositive()) { cLog(lsWARNING) << "OfferCreate: delay: Offers must be at least partially funded."; @@ -458,7 +450,7 @@ TER OfferCreateTransactor::doApply() bPassive, uTakeBookBase, mTxnAccountID, - mTxnAccount, + sleCreator, saTakerGets, // Reverse as we are the taker for taking. saTakerPays, saPaid, // How much would have spent at full price. @@ -482,7 +474,7 @@ TER OfferCreateTransactor::doApply() cLog(lsWARNING) << "OfferCreate: takeOffers: saTakerPays=" << saTakerPays.getFullText(); cLog(lsWARNING) << "OfferCreate: takeOffers: saTakerGets=" << saTakerGets.getFullText(); cLog(lsWARNING) << "OfferCreate: takeOffers: mTxnAccountID=" << RippleAddress::createHumanAccountID(mTxnAccountID); - cLog(lsWARNING) << "OfferCreate: takeOffers: FUNDS=" << mEngine->getNodes().accountFunds(mTxnAccountID, saTakerGets).getFullText(); + cLog(lsWARNING) << "OfferCreate: takeOffers: FUNDS=" << lesActive.accountFunds(mTxnAccountID, saTakerGets).getFullText(); // cLog(lsWARNING) << "OfferCreate: takeOffers: uPaysIssuerID=" << RippleAddress::createHumanAccountID(uPaysIssuerID); // cLog(lsWARNING) << "OfferCreate: takeOffers: uGetsIssuerID=" << RippleAddress::createHumanAccountID(uGetsIssuerID); @@ -500,19 +492,19 @@ TER OfferCreateTransactor::doApply() else if (bFillOrKill && (saTakerPays || saTakerGets)) { // Fill or kill and have leftovers. - terResult = tecKILL; + lesActive.swapWith(lesCheckpoint); // Restore with just fees paid. } else if ( !saTakerPays // Wants nothing more. || !saTakerGets // Offering nothing more. - || bImmediateOrCancel // Do not persist. - || !mEngine->getNodes().accountFunds(mTxnAccountID, saTakerGets).isPositive() // Not funded. + || bImmediateOrCancel // Do not persist. + || !lesActive.accountFunds(mTxnAccountID, saTakerGets).isPositive() // Not funded. || bUnfunded) // Consider unfunded. { // Complete as is. nothing(); } - else if (mPriorBalance.getNValue() < mEngine->getLedger()->getReserve(mTxnAccount->getFieldU32(sfOwnerCount)+1)) + else if (mPriorBalance.getNValue() < mEngine->getLedger()->getReserve(sleCreator->getFieldU32(sfOwnerCount)+1)) { if (bOpenLedger) // Ledger is not final, can vote no. { @@ -541,14 +533,14 @@ TER OfferCreateTransactor::doApply() % saTakerGets.getFullText()); // Add offer to owner's directory. - terResult = mEngine->getNodes().dirAdd(uOwnerNode, Ledger::getOwnerDirIndex(mTxnAccountID), uLedgerIndex, + terResult = lesActive.dirAdd(uOwnerNode, Ledger::getOwnerDirIndex(mTxnAccountID), uLedgerIndex, boost::bind(&Ledger::qualityDirDescriber, _1, saTakerPays.getCurrency(), uPaysIssuerID, saTakerGets.getCurrency(), uGetsIssuerID, uRate)); if (tesSUCCESS == terResult) { - mEngine->getNodes().ownerCountAdjust(mTxnAccountID, 1, mTxnAccount); // Update owner count. + lesActive.ownerCountAdjust(mTxnAccountID, 1, sleCreator); // Update owner count. uint256 uBookBase = Ledger::getBookBase(uPaysCurrency, uPaysIssuerID, uGetsCurrency, uGetsIssuerID); @@ -562,7 +554,7 @@ TER OfferCreateTransactor::doApply() uDirectory = Ledger::getQualityIndex(uBookBase, uRate); // Use original rate. // Add offer to order book. - terResult = mEngine->getNodes().dirAdd(uBookNode, uDirectory, uLedgerIndex, + terResult = lesActive.dirAdd(uBookNode, uDirectory, uLedgerIndex, boost::bind(&Ledger::qualityDirDescriber, _1, saTakerPays.getCurrency(), uPaysIssuerID, saTakerGets.getCurrency(), uGetsIssuerID, uRate)); } @@ -599,6 +591,20 @@ TER OfferCreateTransactor::doApply() } } + // On storing meta data, delete offers that were found unfunded to prevent encountering them in future. + if (tesSUCCESS == terResult) + { + BOOST_FOREACH(const uint256& uOfferIndex, usOfferUnfundedFound) + { + + cLog(lsINFO) << "takeOffers: found unfunded: " << uOfferIndex.ToString(); + + terResult = lesActive.offerDelete(uOfferIndex); + if (tesSUCCESS != terResult) + break; + } + } + tLog(tesSUCCESS != terResult, lsINFO) << boost::str(boost::format("OfferCreate: final terResult=%s") % transToken(terResult)); if (isTesSuccess(terResult)) diff --git a/src/cpp/ripple/OfferCreateTransactor.h b/src/cpp/ripple/OfferCreateTransactor.h index a5c3a8d3d..0947c656f 100644 --- a/src/cpp/ripple/OfferCreateTransactor.h +++ b/src/cpp/ripple/OfferCreateTransactor.h @@ -5,6 +5,7 @@ class OfferCreateTransactor : public Transactor { +protected: TER takeOffers( const bool bOpenLedger, const bool bPassive, @@ -17,6 +18,8 @@ class OfferCreateTransactor : public Transactor STAmount& saTakerGot, bool& bUnfunded); + boost::unordered_set usOfferUnfundedFound; // Offers found unfunded. + public: OfferCreateTransactor(const SerializedTransaction& txn,TransactionEngineParams params, TransactionEngine* engine) : Transactor(txn,params,engine) {} TER doApply(); diff --git a/src/cpp/ripple/Peer.cpp b/src/cpp/ripple/Peer.cpp index 62bc160cf..991afb52d 100644 --- a/src/cpp/ripple/Peer.cpp +++ b/src/cpp/ripple/Peer.cpp @@ -34,6 +34,8 @@ Peer::Peer(boost::asio::io_service& io_service, boost::asio::ssl::context& ctx, mActive(2), mCluster(false), mPeerId(peerID), + mPrivate(false), + mLoad(""), mSocketSsl(io_service, ctx), mActivityTimer(io_service) { @@ -77,6 +79,7 @@ void Peer::handleWrite(const boost::system::error_code& error, size_t bytes_tran void Peer::setIpPort(const std::string& strIP, int iPort) { mIpPort = make_pair(strIP, iPort); + mLoad.rename(strIP); cLog(lsDEBUG) << "Peer: Set: " << ADDRESS(this) << "> " diff --git a/src/cpp/ripple/RPCHandler.cpp b/src/cpp/ripple/RPCHandler.cpp index 412853584..67ebec782 100644 --- a/src/cpp/ripple/RPCHandler.cpp +++ b/src/cpp/ripple/RPCHandler.cpp @@ -63,12 +63,13 @@ int iAdminGet(const Json::Value& jvRequest, const std::string& strRemoteIp) return iRole; } -RPCHandler::RPCHandler(NetworkOPs* netOps) +RPCHandler::RPCHandler(NetworkOPs* netOps, LoadSource &ls) : mLoadSource(ls) { mNetOps = netOps; } -RPCHandler::RPCHandler(NetworkOPs* netOps, InfoSub::pointer infoSub) : mInfoSub(infoSub) +RPCHandler::RPCHandler(NetworkOPs* netOps, InfoSub::pointer infoSub, LoadSource& ls) + : mInfoSub(infoSub), mLoadSource(ls) { mNetOps = netOps; } @@ -885,7 +886,6 @@ Json::Value RPCHandler::doAccountLines(Json::Value jvRequest) if (!lpLedger) return jvResult; - if (!jvRequest.isMember("account")) return rpcError(rpcINVALID_PARAMS); @@ -1153,10 +1153,14 @@ Json::Value RPCHandler::doRandom(Json::Value jvRequest) // - From a trusted server, allows clients to use path without manipulation. Json::Value RPCHandler::doRipplePathFind(Json::Value jvRequest) { - Json::Value jvResult(Json::objectValue); RippleAddress raSrc; RippleAddress raDst; STAmount saDstAmount; + Ledger::pointer lpLedger; + Json::Value jvResult = lookupLedger(jvRequest, lpLedger); + + if (!lpLedger) + return jvResult; if (theApp->getJobQueue().getJobCountGE(jtCLIENT) > 200) { @@ -1201,7 +1205,6 @@ Json::Value RPCHandler::doRipplePathFind(Json::Value jvRequest) } else { - Ledger::pointer lpCurrent = mNetOps->getCurrentLedger(); Json::Value jvSrcCurrencies; if (jvRequest.isMember("source_currencies")) @@ -1210,7 +1213,7 @@ Json::Value RPCHandler::doRipplePathFind(Json::Value jvRequest) } else { - boost::unordered_set usCurrencies = usAccountSourceCurrencies(raSrc, lpCurrent); + boost::unordered_set usCurrencies = usAccountSourceCurrencies(raSrc, lpLedger); // Add XRP as a source currency. // YYY Only bother if they are above reserve. @@ -1228,7 +1231,7 @@ Json::Value RPCHandler::doRipplePathFind(Json::Value jvRequest) } } - Ledger::pointer lSnapShot = boost::make_shared(boost::ref(*lpCurrent), false); + Ledger::pointer lSnapShot = boost::make_shared(boost::ref(*lpLedger), false); LedgerEntrySet lesSnapshot(lSnapShot); ScopedUnlock su(theApp->getMasterLock()); // As long as we have a locked copy of the ledger, we can unlock. @@ -2020,7 +2023,7 @@ Json::Value RPCHandler::doGetCounts(Json::Value jvRequest) int s = upTime(); textTime(uptime, s, "year", 365*24*60*60); textTime(uptime, s, "day", 24*60*60); - textTime(uptime, s, "hour", 24*60); + textTime(uptime, s, "hour", 60*60); textTime(uptime, s, "minute", 60); textTime(uptime, s, "second", 1); ret["uptime"] = uptime; diff --git a/src/cpp/ripple/RPCHandler.h b/src/cpp/ripple/RPCHandler.h index 48fe696ce..9c8c3e288 100644 --- a/src/cpp/ripple/RPCHandler.h +++ b/src/cpp/ripple/RPCHandler.h @@ -18,6 +18,7 @@ class RPCHandler NetworkOPs* mNetOps; InfoSub::pointer mInfoSub; int mRole; + LoadSource& mLoadSource; typedef Json::Value (RPCHandler::*doFuncPtr)(Json::Value params); enum { @@ -113,8 +114,8 @@ public: enum { GUEST, USER, ADMIN, FORBID }; - RPCHandler(NetworkOPs* netOps); - RPCHandler(NetworkOPs* netOps, InfoSub::pointer infoSub); + RPCHandler(NetworkOPs* netOps, LoadSource&); + RPCHandler(NetworkOPs* netOps, InfoSub::pointer infoSub, LoadSource&); Json::Value doCommand(const Json::Value& jvRequest, int role); Json::Value doRpcCommand(const std::string& strCommand, Json::Value& jvParams, int iRole); diff --git a/src/cpp/ripple/RPCServer.cpp b/src/cpp/ripple/RPCServer.cpp index cd7474261..33185c3b7 100644 --- a/src/cpp/ripple/RPCServer.cpp +++ b/src/cpp/ripple/RPCServer.cpp @@ -24,7 +24,7 @@ SETUP_LOG(); #endif RPCServer::RPCServer(boost::asio::io_service& io_service , NetworkOPs* nopNetwork) - : mNetOps(nopNetwork), mSocket(io_service) + : mNetOps(nopNetwork), mLoadSource("rpc"), mSocket(io_service) { mRole = RPCHandler::GUEST; } @@ -51,7 +51,7 @@ void RPCServer::handle_read_req(const boost::system::error_code& e) if (!HTTPAuthorized(mHTTPRequest.peekHeaders())) mReplyStr = HTTPReply(403, "Forbidden"); else - mReplyStr = handleRequest(req); + mReplyStr = handleRequest(req, mLoadSource); boost::asio::async_write(mSocket, boost::asio::buffer(mReplyStr), boost::bind(&RPCServer::handle_write, shared_from_this(), boost::asio::placeholders::error)); @@ -110,7 +110,7 @@ void RPCServer::handle_read_line(const boost::system::error_code& e) } } -std::string RPCServer::handleRequest(const std::string& requestStr) +std::string RPCServer::handleRequest(const std::string& requestStr, LoadSource& ls) { cLog(lsTRACE) << "handleRequest " << requestStr; @@ -154,7 +154,7 @@ std::string RPCServer::handleRequest(const std::string& requestStr) return HTTPReply(403, "Forbidden"); } - RPCHandler mRPCHandler(mNetOps); + RPCHandler mRPCHandler(mNetOps, mLoadSource); cLog(lsTRACE) << valParams; Json::Value result = mRPCHandler.doRpcCommand(strMethod, valParams, mRole); diff --git a/src/cpp/ripple/RPCServer.h b/src/cpp/ripple/RPCServer.h index c96774354..bfc6a4c0e 100644 --- a/src/cpp/ripple/RPCServer.h +++ b/src/cpp/ripple/RPCServer.h @@ -13,6 +13,7 @@ #include "NetworkOPs.h" #include "SerializedLedger.h" #include "RPCHandler.h" +#include "LoadManager.h" class RPCServer : public boost::enable_shared_from_this { @@ -23,6 +24,7 @@ public: private: NetworkOPs* mNetOps; + LoadSource mLoadSource; boost::asio::ip::tcp::socket mSocket; @@ -44,7 +46,7 @@ private: void handle_read_line(const boost::system::error_code& ec); void handle_read_req(const boost::system::error_code& ec); - std::string handleRequest(const std::string& requestStr); + std::string handleRequest(const std::string& requestStr, LoadSource& ls); public: static pointer create(boost::asio::io_service& io_service, NetworkOPs* mNetOps) diff --git a/src/cpp/ripple/RippleCalc.cpp b/src/cpp/ripple/RippleCalc.cpp index c638a869a..e518739dd 100644 --- a/src/cpp/ripple/RippleCalc.cpp +++ b/src/cpp/ripple/RippleCalc.cpp @@ -1,7 +1,7 @@ // TODO: // - Do automatic bridging via XRP. // -// OPTIMIZE: When calculating path increment, note if increment consumes all liquidity. No need to revesit path in the future if +// OPTIMIZE: When calculating path increment, note if increment consumes all liquidity. No need to revisit path in the future if // all liquidity is used. // @@ -270,7 +270,7 @@ TER PathState::pushNode( // Insert intermediary issuer account if needed. terResult = pushImply( - ACCOUNT_XRP, // Rippling, but offer's don't have an account. + ACCOUNT_XRP, // Rippling, but offers don't have an account. pnPrv.uCurrencyID, pnPrv.uIssuerID); } @@ -384,7 +384,7 @@ void PathState::setExpanded( if (tesSUCCESS == terStatus && !!uOutCurrencyID // Next is not XRP - && uOutIssuerID != uReceiverID // Out issuer is not reciever + && uOutIssuerID != uReceiverID // Out issuer is not receiver && (pnPrv.uCurrencyID != uOutCurrencyID // Previous will be an offer. || pnPrv.uAccountID != uOutIssuerID)) // Need the implied issuer. { @@ -427,12 +427,7 @@ void PathState::setExpanded( { const PaymentNode& pnCur = vpnNodes[uNode]; - if (!!pnCur.uAccountID) - { - // Source is a ripple line - nothing(); - } - else if (!umForward.insert(std::make_pair(boost::make_tuple(pnCur.uAccountID, pnCur.uCurrencyID, pnCur.uIssuerID), uNode)).second) + if (!umForward.insert(std::make_pair(boost::make_tuple(pnCur.uAccountID, pnCur.uCurrencyID, pnCur.uIssuerID), uNode)).second) { // Failed to insert. Have a loop. cLog(lsDEBUG) << boost::str(boost::format("PathState: loop detected: %s") @@ -472,7 +467,7 @@ void PathState::setExpanded( // Optimization: // - An XRP output implies an offer node or destination node is next. // - A change in currency implies an offer node. -// - A change in issuer... +// - A change in issuer... void PathState::setCanonical( const PathState& psExpanded ) @@ -496,7 +491,7 @@ void PathState::setCanonical( uint160 uCurrencyID = uMaxCurrencyID; uint160 uIssuerID = uMaxIssuerID; - // Node 0 is a composit of the sending account and saInAct. + // Node 0 is a composite of the sending account and saInAct. ++uNode; // skip node 0 // Last node is implied: Always skip last node @@ -829,8 +824,8 @@ TER RippleCalc::calcNodeAdvance( uDirectEnd = Ledger::getQualityNext(uDirectTip); sleDirectDir = lesActive.entryCache(ltDIR_NODE, uDirectTip); - bDirectAdvance = !sleDirectDir; - bDirectDirDirty = true; + bDirectDirDirty = !!sleDirectDir; // Associated vars are dirty, if found it. + bDirectAdvance = !sleDirectDir; // Advance, if didn't find it. Normal not to be unable to lookup firstdirectory. Maybe even skip this lookup. cLog(lsTRACE) << boost::str(boost::format("calcNodeAdvance: Initialize node: uDirectTip=%s uDirectEnd=%s bDirectAdvance=%d") % uDirectTip % uDirectEnd % bDirectAdvance); } @@ -879,6 +874,7 @@ TER RippleCalc::calcNodeAdvance( { if (bFundsDirty) { + // We were called again probably merely to update structure variables. saTakerPays = sleOffer->getFieldAmount(sfTakerPays); saTakerGets = sleOffer->getFieldAmount(sfTakerGets); @@ -897,22 +893,27 @@ TER RippleCalc::calcNodeAdvance( { // Failed to find an entry in directory. - uOfferIndex = 0; - // Do another cur directory iff bMultiQuality if (bMultiQuality) { + // We are allowed to process multiple qualities if this is the only path. cLog(lsTRACE) << boost::str(boost::format("calcNodeAdvance: next quality")); - bDirectAdvance = true; + + bDirectAdvance = true; // Process next quality. } else if (!bReverse) { cLog(lsWARNING) << boost::str(boost::format("calcNodeAdvance: unreachable: ran out of offers")); assert(false); // Can't run out of offers in forward direction. - terResult = tefEXCEPTION; + terResult = tefEXCEPTION; } else - bEntryAdvance = false; + { + // Ran off end of offers. + + bEntryAdvance = false; // Done. + uOfferIndex = 0; // Report nore more entries. + } } else { @@ -932,20 +933,38 @@ TER RippleCalc::calcNodeAdvance( cLog(lsTRACE) << "calcNodeAdvance: expired offer"; assert(musUnfundedFound.find(uOfferIndex) != musUnfundedFound.end()); // Verify reverse found it too. - bEntryAdvance = true; + // Just skip it. It will be deleted. + // bEntryAdvance = true; // Already set continue; } else if (!saTakerPays.isPositive() || !saTakerGets.isPositive()) { - // Offer is has bad amounts. - cLog(lsWARNING) << boost::str(boost::format("calcNodeAdvance: NON-POSITIVE: saTakerPays=%s saTakerGets=%s") - % saTakerPays % saTakerGets); + // Offer has bad amounts. Offers should never have a bad amounts. - // assert(musUnfundedFound.find(uOfferIndex) != musUnfundedFound.end()); // Verify reverse found it too. - bEntryAdvance = true; + if (musUnfundedFound.find(uOfferIndex) != musUnfundedFound.end()) + { + // An internal error, offer was found failed to place this in musUnfundedFound. + cLog(lsWARNING) << boost::str(boost::format("calcNodeAdvance: PAST INTERNAL ERROR: OFFER NON-POSITIVE: saTakerPays=%s saTakerGets=%s") + % saTakerPays % saTakerGets); + + // Just skip it. It will be deleted. + // bEntryAdvance = true; // Already set + } + else + { + // Reverse should have previously put bad offer in list. + // An internal error previously left a bad offer. + cLog(lsWARNING) << boost::str(boost::format("calcNodeAdvance: INTERNAL ERROR: OFFER NON-POSITIVE: saTakerPays=%s saTakerGets=%s") + % saTakerPays % saTakerGets); +//assert(false); + // Don't process at all, things are in an unexpected state for this transactions. + terResult = tefEXCEPTION; + } continue; } + bEntryAdvance = false; + // 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 @@ -953,6 +972,8 @@ TER RippleCalc::calcNodeAdvance( 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) { // Temporarily unfunded. Another node uses this source, ignore in this offer. @@ -962,21 +983,12 @@ TER RippleCalc::calcNodeAdvance( continue; } - curIssuerNodeConstIterator itPast = mumSource.find(asLine); - bool bFoundPast = itPast != mumSource.end(); - - if (bFoundPast && itPast->second != uNode) - { - // Temporarily unfunded. Another node uses this source, ignore in this offer. - cLog(lsTRACE) << "calcNodeAdvance: temporarily unfunded offer (past)"; - - bEntryAdvance = true; - 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) { // Temporarily unfunded. Another node uses this source, ignore in this offer. @@ -986,7 +998,23 @@ TER RippleCalc::calcNodeAdvance( continue; } - saOfferFunds = lesActive.accountFunds(uOfrOwnerID, saTakerGets); // Funds left. + curIssuerNodeConstIterator itPast = mumSource.find(asLine); + bool bFoundPast = itPast != mumSource.end(); + + // Determine if used in past. + // XXX Restriction seems like a misunderstanding. + if (bFoundPast && itPast->second != uNode) + { + // Temporarily unfunded. Another node uses this source, ignore in this offer. + cLog(lsTRACE) << "calcNodeAdvance: temporarily unfunded offer (past)"; + + bEntryAdvance = true; + continue; + } + + // Only the current node is allowed to use the source. + + saOfferFunds = lesActive.accountFunds(uOfrOwnerID, saTakerGets); // Funds held. if (!saOfferFunds.isPositive()) { @@ -995,9 +1023,17 @@ TER RippleCalc::calcNodeAdvance( if (bReverse && !bFoundReverse && !bFoundPast) { - // Never mentioned before: found unfunded. + // 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 check again. + // Or source was used this reverse + // Or source was previously used + // XXX + } // YYY Could verify offer is correct place for unfundeds. bEntryAdvance = true; @@ -1301,9 +1337,16 @@ TER RippleCalc::calcNodeDeliverFwd( saInAct.zero(saInReq); saInFees.zero(saInReq); + int loopCount = 0; while (tesSUCCESS == terResult && saInAct + saInFees != saInReq) // Did not deliver all funds. { + if (++loopCount > 40) + { + cLog(lsWARNING) << "max loops cndf"; + return mOpenLedger ? telFAILED_PROCESSING : tecFAILED_PROCESSING; + } + // Determine values for pass to adjust saInAct, saInFees, and saCurDeliverAct terResult = calcNodeAdvance(uNode, psCur, bMultiQuality, false); // If needed, advance to next funded offer. @@ -2427,7 +2470,7 @@ void RippleCalc::pathNext(PathState::ref psrCur, const bool bMultiQuality, const assert(psrCur->vpnNodes.size() >= 2); lesCurrent = lesCheckpoint; // Restore from checkpoint. - lesCurrent.bumpSeq(); // Begin ledger varance. + lesCurrent.bumpSeq(); // Begin ledger variance. psrCur->terStatus = calcNodeRev(uLast, *psrCur, bMultiQuality); @@ -2437,7 +2480,7 @@ void RippleCalc::pathNext(PathState::ref psrCur, const bool bMultiQuality, const { // Do forward. lesCurrent = lesCheckpoint; // Restore from checkpoint. - lesCurrent.bumpSeq(); // Begin ledger varance. + lesCurrent.bumpSeq(); // Begin ledger variance. psrCur->terStatus = calcNodeFwd(0, *psrCur, bMultiQuality); } @@ -2895,7 +2938,7 @@ void TransactionEngine::calcOfferBridgeNext( { // Offer fully funded. - // Account transfering funds in to offer always pays inbound fees. + // Account transferring funds in to offer always pays inbound fees. saOfferIn = saOfferGets; // XXX Add in fees? @@ -2934,26 +2977,26 @@ void TransactionEngine::calcOfferBridgeNext( // - reverse: prv is maximum to pay in (including fee) - cur is what is wanted: generally, minimizing prv // - forward: prv is actual amount to pay in (including fee) - cur is what is wanted: generally, minimizing cur // Value in is may be rippled or credited from limbo. Value out is put in limbo. -// If next is an offer, the amount needed is in cur reedem. +// If next is an offer, the amount needed is in cur redeem. // XXX What about account mentioned multiple times via offers? void TransactionEngine::calcNodeOffer( bool bForward, bool bMultiQuality, // True, if this is the only active path: we can do multiple qualities in this pass. - const uint160& uPrvAccountID, // If 0, then funds from previous offer's limbo + const uint160& uPrvAccountID, // If 0, then funds from previous offers limbo const uint160& uPrvCurrencyID, const uint160& uPrvIssuerID, const uint160& uCurCurrencyID, const uint160& uCurIssuerID, const STAmount& uPrvRedeemReq, // --> In limit. - STAmount& uPrvRedeemAct, // <-> In limit achived. + STAmount& uPrvRedeemAct, // <-> In limit achieved. const STAmount& uCurRedeemReq, // --> Out limit. Driver when uCurIssuerID == uNxtIssuerID (offer would redeem to next) - STAmount& uCurRedeemAct, // <-> Out limit achived. + STAmount& uCurRedeemAct, // <-> Out limit achieved. const STAmount& uCurIssueReq, // --> In limit. - STAmount& uCurIssueAct, // <-> In limit achived. + STAmount& uCurIssueAct, // <-> In limit achieved. const STAmount& uCurIssueReq, // --> Out limit. Driver when uCurIssueReq != uNxtIssuerID (offer would effectively issue or transfer to next) - STAmount& uCurIssueAct, // <-> Out limit achived. + STAmount& uCurIssueAct, // <-> Out limit achieved. STAmount& saPay, STAmount& saGot @@ -3007,13 +3050,13 @@ void TransactionEngine::calcNodeOffer( bool bRedeeming = false; bool bIssuing = false; - // The price varies as we change between issuing and transfering, so unless bMultiQuality, we must stick with a mode once it + // The price varies as we change between issuing and transferring, so unless bMultiQuality, we must stick with a mode once it // is determined. if (bBridge && (bInNext || bOutNext)) { // Bridging and need to calculate next bridge rate. - // A bridge can consist of multiple offers. As offer's are consumed, the effective rate changes. + // A bridge can consist of multiple offers. As offers are consumed, the effective rate changes. if (bInNext) { diff --git a/src/cpp/ripple/TransactionErr.h b/src/cpp/ripple/TransactionErr.h index 05c5c9ad6..a1d9fb690 100644 --- a/src/cpp/ripple/TransactionErr.h +++ b/src/cpp/ripple/TransactionErr.h @@ -123,7 +123,6 @@ enum TER // aka TransactionEngineResult tecUNFUNDED_OFFER = 103, tecUNFUNDED_PAYMENT = 104, tecFAILED_PROCESSING = 105, - tecKILL = 106, // tesSUCCESS is not retryable. tecDIR_FULL = 121, tecINSUF_RESERVE_LINE = 122, tecINSUF_RESERVE_OFFER = 123, diff --git a/src/cpp/ripple/WSConnection.h b/src/cpp/ripple/WSConnection.h index 399c6a827..7ce02fba4 100644 --- a/src/cpp/ripple/WSConnection.h +++ b/src/cpp/ripple/WSConnection.h @@ -14,6 +14,7 @@ #include "CallRPC.h" #include "InstanceCounter.h" #include "Log.h" +#include "LoadManager.h" #include "RPCErr.h" DEFINE_INSTANCE(WebSocketConnection); @@ -45,6 +46,7 @@ protected: weak_connection_ptr mConnection; NetworkOPs& mNetwork; std::string mRemoteIP; + LoadSource mLoadSource; boost::asio::deadline_timer mPingTimer; bool mPinged; @@ -56,9 +58,9 @@ public: WSConnection(WSServerHandler* wshpHandler, const connection_ptr& cpConnection) : mHandler(wshpHandler), mConnection(cpConnection), mNetwork(theApp->getOPs()), - mPingTimer(cpConnection->get_io_service()), mPinged(false) + mRemoteIP(cpConnection->get_socket().lowest_layer().remote_endpoint().address().to_string()), + mLoadSource(mRemoteIP), mPingTimer(cpConnection->get_io_service()), mPinged(false) { - mRemoteIP = cpConnection->get_socket().lowest_layer().remote_endpoint().address().to_string(); cLog(lsDEBUG) << "Websocket connection from " << mRemoteIP; setPingTimer(); } @@ -103,7 +105,8 @@ public: return jvResult; } - RPCHandler mRPCHandler(&mNetwork, boost::shared_polymorphic_downcast(this->shared_from_this())); + RPCHandler mRPCHandler(&mNetwork, + boost::shared_polymorphic_downcast(this->shared_from_this()), mLoadSource); Json::Value jvResult(Json::objectValue); int iRole = mHandler->getPublic() diff --git a/src/cpp/ripple/main.cpp b/src/cpp/ripple/main.cpp index a69c9629e..1c114aa07 100644 --- a/src/cpp/ripple/main.cpp +++ b/src/cpp/ripple/main.cpp @@ -43,7 +43,8 @@ void startServer() if (!theConfig.QUIET) std::cerr << "Startup RPC: " << jvCommand << std::endl; - RPCHandler rhHandler(&theApp->getOPs()); + LoadSource ls(true); + RPCHandler rhHandler(&theApp->getOPs(), ls); Json::Value jvResult = rhHandler.doCommand(jvCommand, RPCHandler::ADMIN); @@ -92,6 +93,7 @@ void printHelp(const po::options_description& desc) cerr << " peers" << endl; cerr << " random" << endl; cerr << " ripple ..." << endl; + cerr << " ripple_path_find []" << endl; // cerr << " send [] [] []" << endl; cerr << " stop" << endl; cerr << " tx " << endl; diff --git a/src/js/sjcl b/src/js/sjcl index dbdef434e..d04d0bdcc 160000 --- a/src/js/sjcl +++ b/src/js/sjcl @@ -1 +1 @@ -Subproject commit dbdef434e76c3f16835f3126a7ff1c717b1ce8af +Subproject commit d04d0bdccd986e434b98fe393e1e01286c10fc36