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