diff --git a/src/cpp/database/SqliteDatabase.cpp b/src/cpp/database/SqliteDatabase.cpp index 1033dcf4e3..7db0a623f6 100644 --- a/src/cpp/database/SqliteDatabase.cpp +++ b/src/cpp/database/SqliteDatabase.cpp @@ -234,6 +234,7 @@ void SqliteDatabase::runWal() std::set walSet; std::string name = sqlite3_db_filename(mConnection, "main"); + int pass = 1; while (1) { { @@ -255,8 +256,11 @@ void SqliteDatabase::runWal() cLog((ret == SQLITE_LOCKED) ? lsTRACE : lsWARNING) << "WAL " << mHost << ":" << db << " error " << ret; } + else + cLog(lsTRACE) << "WAL(" << mHost << "): pass=" << pass << ", frames=" << log << ", written=" << ckpt; } walSet.clear(); + ++pass; } } diff --git a/src/cpp/ripple/LoadManager.cpp b/src/cpp/ripple/LoadManager.cpp index 87b2237a4b..1eaad6c6bc 100644 --- a/src/cpp/ripple/LoadManager.cpp +++ b/src/cpp/ripple/LoadManager.cpp @@ -132,24 +132,31 @@ void LoadManager::canonicalize(LoadSource& source, int now) const bool LoadManager::shouldWarn(LoadSource& source) const { - boost::mutex::scoped_lock sl(mLock); + { + boost::mutex::scoped_lock sl(mLock); - int now = upTime(); - canonicalize(source, now); - if (source.isPrivileged() || (source.mBalance < mDebitWarn) || (source.mLastWarning == now)) - return false; + int now = upTime(); + canonicalize(source, now); + if (source.isPrivileged() || (source.mBalance < mDebitWarn) || (source.mLastWarning == now)) + return false; - source.mLastWarning = now; + source.mLastWarning = now; + } + logWarning(source.getName()); return true; } bool LoadManager::shouldCutoff(LoadSource& source) const { - boost::mutex::scoped_lock sl(mLock); - - int now = upTime(); - canonicalize(source, now); - return !source.isPrivileged() && (source.mBalance < mDebitLimit); + { + boost::mutex::scoped_lock sl(mLock); + int now = upTime(); + canonicalize(source, now); + if (!source.isPrivileged() || (source.mBalance > mDebitLimit)) + return false; + } + logDisconnect(source.getName()); + return true; } bool LoadManager::adjust(LoadSource& source, LoadType t) const @@ -160,7 +167,6 @@ bool LoadManager::adjust(LoadSource& source, LoadType t) const bool LoadManager::adjust(LoadSource& source, int credits) const { // return: true = need to warn/cutoff - boost::mutex::scoped_lock sl(mLock); // We do it this way in case we want to add exponential decay later int now = upTime(); @@ -172,13 +178,10 @@ bool LoadManager::adjust(LoadSource& source, int credits) const if (source.isPrivileged()) // privileged sources never warn/cutoff return false; - if (source.mBalance < mDebitLimit) // over-limit - return true; + if ((source.mBalance >= mDebitLimit) && (source.mLastWarning == now)) // no need to warn + return false; - if ((source.mBalance < mDebitWarn) && (source.mLastWarning != now)) // need to warn - return true; - - return false; + return true; } uint64 LoadFeeTrack::mulDiv(uint64 value, uint32 mul, uint64 div) @@ -345,6 +348,22 @@ void LoadManager::threadEntry() } } +void LoadManager::logWarning(const std::string& source) const +{ + if (source.empty()) + cLog(lsDEBUG) << "Load warning from empty source"; + else + cLog(lsINFO) << "Load warning: " << source; +} + +void LoadManager::logDisconnect(const std::string& source) const +{ + if (source.empty()) + cLog(lsINFO) << "Disconnect for empty source"; + else + cLog(lsWARNING) << "Disconnect for: " << source; +} + BOOST_AUTO_TEST_SUITE(LoadManager_test) BOOST_AUTO_TEST_CASE(LoadFeeTrack_test) diff --git a/src/cpp/ripple/LoadManager.h b/src/cpp/ripple/LoadManager.h index 8899bf575b..070384d702 100644 --- a/src/cpp/ripple/LoadManager.h +++ b/src/cpp/ripple/LoadManager.h @@ -73,8 +73,8 @@ public: LoadSource(const std::string& name) : mName(name), mBalance(0), mFlags(0), mLastUpdate(upTime()), mLastWarning(0) { ; } - void rename(const std::string& name) - { mName = name; } + void rename(const std::string& name) { mName = name; } + const std::string& getName() { return mName; } bool isPrivileged() const { return (mFlags & lsfPrivileged) != 0; } void setPrivileged() { mFlags |= lsfPrivileged; } @@ -112,7 +112,7 @@ protected: public: - LoadManager(int creditRate = 10, int creditLimit = 50, int debitWarn = -50, int debitLimit = -100); + LoadManager(int creditRate = 100, int creditLimit = 500, int debitWarn = -500, int debitLimit = -1000); ~LoadManager(); void init(); @@ -130,6 +130,9 @@ public: bool adjust(LoadSource&, int credits) const; // return value: false=balance okay, true=warn/cutoff bool adjust(LoadSource&, LoadType l) const; + void logWarning(const std::string&) const; + void logDisconnect(const std::string&) const; + int getCost(LoadType t) { return mCosts[static_cast(t)].mCost; } int getUptime(); }; diff --git a/src/cpp/ripple/RPCHandler.cpp b/src/cpp/ripple/RPCHandler.cpp index 67ebec7822..76d135017a 100644 --- a/src/cpp/ripple/RPCHandler.cpp +++ b/src/cpp/ripple/RPCHandler.cpp @@ -25,6 +25,10 @@ SETUP_LOG(); +static const int rpcCOST_DEFAULT = 10; +static const int rpcCOST_EXCEPTION = 20; +static const int rpcCOST_EXPENSIVE = 50; + int iAdminGet(const Json::Value& jvRequest, const std::string& strRemoteIp) { int iRole; @@ -63,16 +67,11 @@ int iAdminGet(const Json::Value& jvRequest, const std::string& strRemoteIp) return iRole; } -RPCHandler::RPCHandler(NetworkOPs* netOps, LoadSource &ls) : mLoadSource(ls) -{ - mNetOps = netOps; -} +RPCHandler::RPCHandler(NetworkOPs* netOps) : mNetOps(netOps) +{ ; } -RPCHandler::RPCHandler(NetworkOPs* netOps, InfoSub::pointer infoSub, LoadSource& ls) - : mInfoSub(infoSub), mLoadSource(ls) -{ - mNetOps = netOps; -} +RPCHandler::RPCHandler(NetworkOPs* netOps, InfoSub::pointer infoSub) : mNetOps(netOps), mInfoSub(infoSub) +{ ; } Json::Value RPCHandler::transactionSign(Json::Value jvRequest, bool bSubmit) { @@ -566,7 +565,7 @@ Json::Value RPCHandler::accountFromString(Ledger::ref lrLedger, RippleAddress& n // ledger_hash : // ledger_index : // } -Json::Value RPCHandler::doAccountInfo(Json::Value jvRequest) +Json::Value RPCHandler::doAccountInfo(Json::Value jvRequest, int& cost) { Ledger::pointer lpLedger; Json::Value jvResult = lookupLedger(jvRequest, lpLedger); @@ -611,7 +610,7 @@ Json::Value RPCHandler::doAccountInfo(Json::Value jvRequest) // port: // } // XXX Might allow domain for manual connections. -Json::Value RPCHandler::doConnect(Json::Value jvRequest) +Json::Value RPCHandler::doConnect(Json::Value jvRequest, int& cost) { if (theConfig.RUN_STANDALONE) return "cannot connect in standalone mode"; @@ -632,7 +631,7 @@ Json::Value RPCHandler::doConnect(Json::Value jvRequest) // { // key: // } -Json::Value RPCHandler::doDataDelete(Json::Value jvRequest) +Json::Value RPCHandler::doDataDelete(Json::Value jvRequest, int& cost) { if (!jvRequest.isMember("key")) return rpcError(rpcINVALID_PARAMS); @@ -658,7 +657,7 @@ Json::Value RPCHandler::doDataDelete(Json::Value jvRequest) // { // key: // } -Json::Value RPCHandler::doDataFetch(Json::Value jvRequest) +Json::Value RPCHandler::doDataFetch(Json::Value jvRequest, int& cost) { if (!jvRequest.isMember("key")) return rpcError(rpcINVALID_PARAMS); @@ -681,7 +680,7 @@ Json::Value RPCHandler::doDataFetch(Json::Value jvRequest) // key: // value: // } -Json::Value RPCHandler::doDataStore(Json::Value jvRequest) +Json::Value RPCHandler::doDataStore(Json::Value jvRequest, int& cost) { if (!jvRequest.isMember("key") || !jvRequest.isMember("value")) @@ -741,7 +740,7 @@ Json::Value RPCHandler::doNicknameInfo(Json::Value params) // 'account_index' : // optional // } // XXX This would be better if it took the ledger. -Json::Value RPCHandler::doOwnerInfo(Json::Value jvRequest) +Json::Value RPCHandler::doOwnerInfo(Json::Value jvRequest, int& cost) { if (!jvRequest.isMember("account") && !jvRequest.isMember("ident")) return rpcError(rpcINVALID_PARAMS); @@ -766,7 +765,7 @@ Json::Value RPCHandler::doOwnerInfo(Json::Value jvRequest) return ret; } -Json::Value RPCHandler::doPeers(Json::Value) +Json::Value RPCHandler::doPeers(Json::Value, int& cost) { Json::Value jvResult(Json::objectValue); @@ -775,7 +774,7 @@ Json::Value RPCHandler::doPeers(Json::Value) return jvResult; } -Json::Value RPCHandler::doPing(Json::Value) +Json::Value RPCHandler::doPing(Json::Value, int& cost) { return Json::Value(Json::objectValue); } @@ -785,7 +784,7 @@ Json::Value RPCHandler::doPing(Json::Value) // issuer is the offering account // --> submit: 'submit|true|false': defaults to false // Prior to running allow each to have a credit line of what they will be getting from the other account. -Json::Value RPCHandler::doProfile(Json::Value jvRequest) +Json::Value RPCHandler::doProfile(Json::Value jvRequest, int& cost) { /* need to fix now that sharedOfferCreate is gone int iArgs = jvRequest.size(); @@ -878,7 +877,7 @@ Json::Value RPCHandler::doProfile(Json::Value jvRequest) // ledger_hash : // ledger_index : // } -Json::Value RPCHandler::doAccountLines(Json::Value jvRequest) +Json::Value RPCHandler::doAccountLines(Json::Value jvRequest, int& cost) { Ledger::pointer lpLedger; Json::Value jvResult = lookupLedger(jvRequest, lpLedger); @@ -957,7 +956,7 @@ Json::Value RPCHandler::doAccountLines(Json::Value jvRequest) // ledger_hash : // ledger_index : // } -Json::Value RPCHandler::doAccountOffers(Json::Value jvRequest) +Json::Value RPCHandler::doAccountOffers(Json::Value jvRequest, int& cost) { Ledger::pointer lpLedger; Json::Value jvResult = lookupLedger(jvRequest, lpLedger); @@ -1028,7 +1027,7 @@ Json::Value RPCHandler::doAccountOffers(Json::Value jvRequest) // "limit" : integer, // Optional. // "proof" : boolean // Defaults to false. // } -Json::Value RPCHandler::doBookOffers(Json::Value jvRequest) +Json::Value RPCHandler::doBookOffers(Json::Value jvRequest, int& cost) { if (theApp->getJobQueue().getJobCountGE(jtCLIENT) > 200) { @@ -1125,7 +1124,7 @@ Json::Value RPCHandler::doBookOffers(Json::Value jvRequest) // { // random: // } -Json::Value RPCHandler::doRandom(Json::Value jvRequest) +Json::Value RPCHandler::doRandom(Json::Value jvRequest, int& cost) { uint256 uRandom; @@ -1151,7 +1150,7 @@ Json::Value RPCHandler::doRandom(Json::Value jvRequest) // - Allows clients to verify path exists. // - Return canonicalized path. // - From a trusted server, allows clients to use path without manipulation. -Json::Value RPCHandler::doRipplePathFind(Json::Value jvRequest) +Json::Value RPCHandler::doRipplePathFind(Json::Value jvRequest, int& cost) { RippleAddress raSrc; RippleAddress raDst; @@ -1231,6 +1230,7 @@ Json::Value RPCHandler::doRipplePathFind(Json::Value jvRequest) } } + cost = rpcCOST_EXPENSIVE; Ledger::pointer lSnapShot = boost::make_shared(boost::ref(*lpLedger), false); LedgerEntrySet lesSnapshot(lSnapShot); @@ -1364,8 +1364,9 @@ Json::Value RPCHandler::doRipplePathFind(Json::Value jvRequest) // tx_json: , // secret: // } -Json::Value RPCHandler::doSign(Json::Value jvRequest) +Json::Value RPCHandler::doSign(Json::Value jvRequest, int& cost) { + cost = rpcCOST_EXPENSIVE; return transactionSign(jvRequest, false); } @@ -1373,7 +1374,7 @@ Json::Value RPCHandler::doSign(Json::Value jvRequest) // tx_json: , // secret: // } -Json::Value RPCHandler::doSubmit(Json::Value jvRequest) +Json::Value RPCHandler::doSubmit(Json::Value jvRequest, int& cost) { if (!jvRequest.isMember("tx_blob")) { @@ -1388,6 +1389,7 @@ Json::Value RPCHandler::doSubmit(Json::Value jvRequest) { return rpcError(rpcINVALID_PARAMS); } + cost = rpcCOST_EXPENSIVE; Serializer sTrans(vucBlob); SerializerIterator sitTrans(sTrans); @@ -1459,7 +1461,7 @@ Json::Value RPCHandler::doSubmit(Json::Value jvRequest) } } -Json::Value RPCHandler::doConsensusInfo(Json::Value) +Json::Value RPCHandler::doConsensusInfo(Json::Value, int& cost) { Json::Value ret(Json::objectValue); @@ -1468,7 +1470,7 @@ Json::Value RPCHandler::doConsensusInfo(Json::Value) return ret; } -Json::Value RPCHandler::doServerInfo(Json::Value) +Json::Value RPCHandler::doServerInfo(Json::Value, int& cost) { Json::Value ret(Json::objectValue); @@ -1477,7 +1479,7 @@ Json::Value RPCHandler::doServerInfo(Json::Value) return ret; } -Json::Value RPCHandler::doServerState(Json::Value) +Json::Value RPCHandler::doServerState(Json::Value, int& cost) { Json::Value ret(Json::objectValue); @@ -1489,7 +1491,7 @@ Json::Value RPCHandler::doServerState(Json::Value) // { // start: // } -Json::Value RPCHandler::doTxHistory(Json::Value jvRequest) +Json::Value RPCHandler::doTxHistory(Json::Value jvRequest, int& cost) { if (!jvRequest.isMember("start")) return rpcError(rpcINVALID_PARAMS); @@ -1523,7 +1525,7 @@ Json::Value RPCHandler::doTxHistory(Json::Value jvRequest) // { // transaction: // } -Json::Value RPCHandler::doTx(Json::Value jvRequest) +Json::Value RPCHandler::doTx(Json::Value jvRequest, int& cost) { if (!jvRequest.isMember("transaction")) return rpcError(rpcINVALID_PARAMS); @@ -1583,7 +1585,7 @@ Json::Value RPCHandler::doTx(Json::Value jvRequest) return rpcError(rpcNOT_IMPL); } -Json::Value RPCHandler::doLedgerClosed(Json::Value) +Json::Value RPCHandler::doLedgerClosed(Json::Value, int& cost) { Json::Value jvResult; @@ -1596,7 +1598,7 @@ Json::Value RPCHandler::doLedgerClosed(Json::Value) return jvResult; } -Json::Value RPCHandler::doLedgerCurrent(Json::Value) +Json::Value RPCHandler::doLedgerCurrent(Json::Value, int& cost) { Json::Value jvResult; @@ -1610,7 +1612,7 @@ Json::Value RPCHandler::doLedgerCurrent(Json::Value) // ledger: 'current' | 'closed' | | , // optional // full: true | false // optional, defaults to false. // } -Json::Value RPCHandler::doLedger(Json::Value jvRequest) +Json::Value RPCHandler::doLedger(Json::Value jvRequest, int& cost) { if (!jvRequest.isMember("ledger")) { @@ -1656,7 +1658,7 @@ Json::Value RPCHandler::doLedger(Json::Value jvRequest) // FIXME: Require admin. // FIXME: Doesn't report database holes. // FIXME: For consistency change inputs to: ledger_index, ledger_index_min, ledger_index_max. -Json::Value RPCHandler::doAccountTransactions(Json::Value jvRequest) +Json::Value RPCHandler::doAccountTransactions(Json::Value jvRequest, int& cost) { RippleAddress raAccount; uint32 minLedger; @@ -1753,7 +1755,7 @@ Json::Value RPCHandler::doAccountTransactions(Json::Value jvRequest) // } // // This command requires admin access because it makes no sense to ask an untrusted server for this. -Json::Value RPCHandler::doValidationCreate(Json::Value jvRequest) { +Json::Value RPCHandler::doValidationCreate(Json::Value jvRequest, int& cost) { RippleAddress raSeed; Json::Value obj(Json::objectValue); @@ -1778,7 +1780,7 @@ Json::Value RPCHandler::doValidationCreate(Json::Value jvRequest) { // { // secret: // } -Json::Value RPCHandler::doValidationSeed(Json::Value jvRequest) { +Json::Value RPCHandler::doValidationSeed(Json::Value jvRequest, int& cost) { Json::Value obj(Json::objectValue); if (!jvRequest.isMember("secret")) @@ -1845,7 +1847,7 @@ Json::Value RPCHandler::accounts(Ledger::ref lrLedger, const RippleAddress& naMa // ledger_hash : // ledger_index : // } -Json::Value RPCHandler::doWalletAccounts(Json::Value jvRequest) +Json::Value RPCHandler::doWalletAccounts(Json::Value jvRequest, int& cost) { Ledger::pointer lpLedger; Json::Value jvResult = lookupLedger(jvRequest, lpLedger); @@ -1888,7 +1890,7 @@ Json::Value RPCHandler::doWalletAccounts(Json::Value jvRequest) } } -Json::Value RPCHandler::doLogRotate(Json::Value) +Json::Value RPCHandler::doLogRotate(Json::Value, int& cost) { return Log::rotateLog(); } @@ -1896,7 +1898,7 @@ Json::Value RPCHandler::doLogRotate(Json::Value) // { // passphrase: // } -Json::Value RPCHandler::doWalletPropose(Json::Value jvRequest) +Json::Value RPCHandler::doWalletPropose(Json::Value jvRequest, int& cost) { RippleAddress naSeed; RippleAddress naAccount; @@ -1926,7 +1928,7 @@ Json::Value RPCHandler::doWalletPropose(Json::Value jvRequest) // { // secret: // } -Json::Value RPCHandler::doWalletSeed(Json::Value jvRequest) +Json::Value RPCHandler::doWalletSeed(Json::Value jvRequest, int& cost) { RippleAddress raSeed; bool bSecret = jvRequest.isMember("secret"); @@ -1965,7 +1967,7 @@ Json::Value RPCHandler::doWalletSeed(Json::Value jvRequest) // username: , // password: // } -Json::Value RPCHandler::doLogin(Json::Value jvRequest) +Json::Value RPCHandler::doLogin(Json::Value jvRequest, int& cost) { if (!jvRequest.isMember("username") || !jvRequest.isMember("password")) @@ -2001,7 +2003,7 @@ static void textTime(std::string& text, int& seconds, const char *unitName, int // { // min_count: // optional, defaults to 10 // } -Json::Value RPCHandler::doGetCounts(Json::Value jvRequest) +Json::Value RPCHandler::doGetCounts(Json::Value jvRequest, int& cost) { int minCount = 10; @@ -2031,7 +2033,7 @@ Json::Value RPCHandler::doGetCounts(Json::Value jvRequest) return ret; } -Json::Value RPCHandler::doLogLevel(Json::Value jvRequest) +Json::Value RPCHandler::doLogLevel(Json::Value jvRequest, int& cost) { // log_level if (!jvRequest.isMember("severity")) @@ -2079,7 +2081,7 @@ Json::Value RPCHandler::doLogLevel(Json::Value jvRequest) // node: |, // comment: // optional // } -Json::Value RPCHandler::doUnlAdd(Json::Value jvRequest) +Json::Value RPCHandler::doUnlAdd(Json::Value jvRequest, int& cost) { std::string strNode = jvRequest.isMember("node") ? jvRequest["node"].asString() : ""; std::string strComment = jvRequest.isMember("comment") ? jvRequest["comment"].asString() : ""; @@ -2103,7 +2105,7 @@ Json::Value RPCHandler::doUnlAdd(Json::Value jvRequest) // { // node: | // } -Json::Value RPCHandler::doUnlDelete(Json::Value jvRequest) +Json::Value RPCHandler::doUnlDelete(Json::Value jvRequest, int& cost) { if (!jvRequest.isMember("node")) return rpcError(rpcINVALID_PARAMS); @@ -2126,7 +2128,7 @@ Json::Value RPCHandler::doUnlDelete(Json::Value jvRequest) } } -Json::Value RPCHandler::doUnlList(Json::Value) +Json::Value RPCHandler::doUnlList(Json::Value, int& cost) { Json::Value obj(Json::objectValue); @@ -2136,7 +2138,7 @@ Json::Value RPCHandler::doUnlList(Json::Value) } // Populate the UNL from a local validators.txt file. -Json::Value RPCHandler::doUnlLoad(Json::Value) +Json::Value RPCHandler::doUnlLoad(Json::Value, int& cost) { if (theConfig.VALIDATORS_FILE.empty() || !theApp->getUNL().nodeLoad(theConfig.VALIDATORS_FILE)) { @@ -2148,7 +2150,7 @@ Json::Value RPCHandler::doUnlLoad(Json::Value) // Populate the UNL from ripple.com's validators.txt file. -Json::Value RPCHandler::doUnlNetwork(Json::Value jvRequest) +Json::Value RPCHandler::doUnlNetwork(Json::Value jvRequest, int& cost) { theApp->getUNL().nodeNetwork(); @@ -2156,7 +2158,7 @@ Json::Value RPCHandler::doUnlNetwork(Json::Value jvRequest) } // unl_reset -Json::Value RPCHandler::doUnlReset(Json::Value jvRequest) +Json::Value RPCHandler::doUnlReset(Json::Value jvRequest, int& cost) { theApp->getUNL().nodeReset(); @@ -2164,21 +2166,21 @@ Json::Value RPCHandler::doUnlReset(Json::Value jvRequest) } // unl_score -Json::Value RPCHandler::doUnlScore(Json::Value) +Json::Value RPCHandler::doUnlScore(Json::Value, int& cost) { theApp->getUNL().nodeScore(); return "scoring requested"; } -Json::Value RPCHandler::doStop(Json::Value) +Json::Value RPCHandler::doStop(Json::Value, int& cost) { theApp->stop(); return SYSTEM_NAME " server stopping"; } -Json::Value RPCHandler::doLedgerAccept(Json::Value) +Json::Value RPCHandler::doLedgerAccept(Json::Value, int& cost) { Json::Value jvResult; @@ -2201,7 +2203,7 @@ Json::Value RPCHandler::doLedgerAccept(Json::Value) // ledger_index : // } // XXX In this case, not specify either ledger does not mean ledger current. It means any ledger. -Json::Value RPCHandler::doTransactionEntry(Json::Value jvRequest) +Json::Value RPCHandler::doTransactionEntry(Json::Value jvRequest, int& cost) { Ledger::pointer lpLedger; Json::Value jvResult = lookupLedger(jvRequest, lpLedger); @@ -2328,7 +2330,7 @@ Json::Value RPCHandler::lookupLedger(Json::Value jvRequest, Ledger::pointer& lpL // ledger_index : // ... // } -Json::Value RPCHandler::doLedgerEntry(Json::Value jvRequest) +Json::Value RPCHandler::doLedgerEntry(Json::Value jvRequest, int& cost) { Ledger::pointer lpLedger; Json::Value jvResult = lookupLedger(jvRequest, lpLedger); @@ -2535,7 +2537,7 @@ Json::Value RPCHandler::doLedgerEntry(Json::Value jvRequest) // ledger_hash : // ledger_index : // } -Json::Value RPCHandler::doLedgerHeader(Json::Value jvRequest) +Json::Value RPCHandler::doLedgerHeader(Json::Value jvRequest, int& cost) { Ledger::pointer lpLedger; Json::Value jvResult = lookupLedger(jvRequest, lpLedger); @@ -2585,7 +2587,7 @@ rt_transactions accounts rt_accounts */ -Json::Value RPCHandler::doSubscribe(Json::Value jvRequest) +Json::Value RPCHandler::doSubscribe(Json::Value jvRequest, int& cost) { InfoSub::pointer ispSub; Json::Value jvResult(Json::objectValue); @@ -2733,7 +2735,7 @@ Json::Value RPCHandler::doSubscribe(Json::Value jvRequest) } // FIXME: This leaks RPCSub objects for JSON-RPC. Shouldn't matter for anyone sane. -Json::Value RPCHandler::doUnsubscribe(Json::Value jvRequest) +Json::Value RPCHandler::doUnsubscribe(Json::Value jvRequest, int& cost) { InfoSub::pointer ispSub; Json::Value jvResult(Json::objectValue); @@ -2846,8 +2848,10 @@ Json::Value RPCHandler::doUnsubscribe(Json::Value jvRequest) // // JSON-RPC provides a method and an array of params. JSON-RPC is used as a transport for a command and a request object. The // command is the method. The request object is supplied as the first element of the params. -Json::Value RPCHandler::doRpcCommand(const std::string& strMethod, Json::Value& jvParams, int iRole) +Json::Value RPCHandler::doRpcCommand(const std::string& strMethod, Json::Value& jvParams, int iRole, int& cost) { + if (cost == 0) + cost = rpcCOST_DEFAULT; cLog(lsTRACE) << "doRpcCommand:" << strMethod << ":" << jvParams; if (!jvParams.isArray() || jvParams.size() > 1) @@ -2861,7 +2865,7 @@ Json::Value RPCHandler::doRpcCommand(const std::string& strMethod, Json::Value& // Provide the JSON-RPC method as the field "command" in the request. jvRequest["command"] = strMethod; - Json::Value jvResult = doCommand(jvRequest, iRole); + Json::Value jvResult = doCommand(jvRequest, iRole, cost); // Always report "status". On an error report the request as received. if (jvResult.isMember("error")) @@ -2876,15 +2880,17 @@ Json::Value RPCHandler::doRpcCommand(const std::string& strMethod, Json::Value& return jvResult; } -Json::Value RPCHandler::doInternal(Json::Value jvRequest) +Json::Value RPCHandler::doInternal(Json::Value jvRequest, int& cost) { // Used for debug or special-purpose RPC commands if (!jvRequest.isMember("internal_command")) return rpcError(rpcINVALID_PARAMS); return RPCInternalHandler::runHandler(jvRequest["internal_command"].asString(), jvRequest["params"]); } -Json::Value RPCHandler::doCommand(const Json::Value& jvRequest, int iRole) +Json::Value RPCHandler::doCommand(const Json::Value& jvRequest, int iRole, int &cost) { + if (cost == 0) + cost = rpcCOST_DEFAULT; if ((iRole != ADMIN) && (theApp->getJobQueue().getJobCountGE(jtCLIENT) > 500)) return rpcError(rpcTOO_BUSY); @@ -3003,7 +3009,7 @@ Json::Value RPCHandler::doCommand(const Json::Value& jvRequest, int iRole) else { try { - Json::Value jvRaw = (this->*(commandsA[i].dfpFunc))(jvRequest); + Json::Value jvRaw = (this->*(commandsA[i].dfpFunc))(jvRequest, cost); // Regularize result. if (jvRaw.isObject()) @@ -3024,6 +3030,8 @@ Json::Value RPCHandler::doCommand(const Json::Value& jvRequest, int iRole) catch (std::exception& e) { cLog(lsINFO) << "Caught throw: " << e.what(); + if (cost == rpcCOST_DEFAULT) + cost = rpcCOST_EXCEPTION; return rpcError(rpcINTERNAL); } diff --git a/src/cpp/ripple/RPCHandler.h b/src/cpp/ripple/RPCHandler.h index 9c8c3e2884..641687ccf1 100644 --- a/src/cpp/ripple/RPCHandler.h +++ b/src/cpp/ripple/RPCHandler.h @@ -18,9 +18,8 @@ class RPCHandler NetworkOPs* mNetOps; InfoSub::pointer mInfoSub; int mRole; - LoadSource& mLoadSource; - typedef Json::Value (RPCHandler::*doFuncPtr)(Json::Value params); + typedef Json::Value (RPCHandler::*doFuncPtr)(Json::Value params, int& cost); enum { optNone = 0, optNetwork = 1, // Need network @@ -44,81 +43,81 @@ class RPCHandler Json::Value accountFromString(Ledger::ref lrLedger, RippleAddress& naAccount, bool& bIndex, const std::string& strIdent, const int iIndex, const bool bStrict); - Json::Value doAccountInfo(Json::Value params); - Json::Value doAccountLines(Json::Value params); - Json::Value doAccountOffers(Json::Value params); - Json::Value doAccountTransactions(Json::Value params); - Json::Value doBookOffers(Json::Value params); - Json::Value doConnect(Json::Value params); - Json::Value doConsensusInfo(Json::Value params); + Json::Value doAccountInfo(Json::Value params, int& cost); + Json::Value doAccountLines(Json::Value params, int& cost); + Json::Value doAccountOffers(Json::Value params, int& cost); + Json::Value doAccountTransactions(Json::Value params, int& cost); + Json::Value doBookOffers(Json::Value params, int& cost); + Json::Value doConnect(Json::Value params, int& cost); + Json::Value doConsensusInfo(Json::Value params, int& cost); #if ENABLE_INSECURE - Json::Value doDataDelete(Json::Value params); - Json::Value doDataFetch(Json::Value params); - Json::Value doDataStore(Json::Value params); + Json::Value doDataDelete(Json::Value params, int& cost); + Json::Value doDataFetch(Json::Value params, int& cost); + Json::Value doDataStore(Json::Value params, int& cost); #endif - Json::Value doGetCounts(Json::Value params); - Json::Value doInternal(Json::Value params); - Json::Value doLedger(Json::Value params); - Json::Value doLogLevel(Json::Value params); - Json::Value doLogRotate(Json::Value params); - Json::Value doNicknameInfo(Json::Value params); - Json::Value doOwnerInfo(Json::Value params); - Json::Value doPeers(Json::Value params); - Json::Value doPing(Json::Value params); - Json::Value doProfile(Json::Value params); - Json::Value doRandom(Json::Value jvRequest); - Json::Value doRipplePathFind(Json::Value jvRequest); - Json::Value doServerInfo(Json::Value params); // for humans - Json::Value doServerState(Json::Value params); // for machines - Json::Value doSessionClose(Json::Value params); - Json::Value doSessionOpen(Json::Value params); - Json::Value doStop(Json::Value params); - Json::Value doSign(Json::Value params); - Json::Value doSubmit(Json::Value params); - Json::Value doTx(Json::Value params); - Json::Value doTxHistory(Json::Value params); - Json::Value doUnlAdd(Json::Value params); - Json::Value doUnlDelete(Json::Value params); - Json::Value doUnlFetch(Json::Value params); - Json::Value doUnlList(Json::Value params); - Json::Value doUnlLoad(Json::Value params); - Json::Value doUnlNetwork(Json::Value params); - Json::Value doUnlReset(Json::Value params); - Json::Value doUnlScore(Json::Value params); + Json::Value doGetCounts(Json::Value params, int& cost); + Json::Value doInternal(Json::Value params, int& cost); + Json::Value doLedger(Json::Value params, int& cost); + Json::Value doLogLevel(Json::Value params, int& cost); + Json::Value doLogRotate(Json::Value params, int& cost); + Json::Value doNicknameInfo(Json::Value params, int& cost); + Json::Value doOwnerInfo(Json::Value params, int& cost); + Json::Value doPeers(Json::Value params, int& cost); + Json::Value doPing(Json::Value params, int& cost); + Json::Value doProfile(Json::Value params, int& cost); + Json::Value doRandom(Json::Value jvRequest, int& cost); + Json::Value doRipplePathFind(Json::Value jvRequest, int& cost); + Json::Value doServerInfo(Json::Value params, int& cost); // for humans + Json::Value doServerState(Json::Value params, int& cost); // for machines + Json::Value doSessionClose(Json::Value params, int& cost); + Json::Value doSessionOpen(Json::Value params, int& cost); + Json::Value doStop(Json::Value params, int& cost); + Json::Value doSign(Json::Value params, int& cost); + Json::Value doSubmit(Json::Value params, int& cost); + Json::Value doTx(Json::Value params, int& cost); + Json::Value doTxHistory(Json::Value params, int& cost); + Json::Value doUnlAdd(Json::Value params, int& cost); + Json::Value doUnlDelete(Json::Value params, int& cost); + Json::Value doUnlFetch(Json::Value params, int& cost); + Json::Value doUnlList(Json::Value params, int& cost); + Json::Value doUnlLoad(Json::Value params, int& cost); + Json::Value doUnlNetwork(Json::Value params, int& cost); + Json::Value doUnlReset(Json::Value params, int& cost); + Json::Value doUnlScore(Json::Value params, int& cost); - Json::Value doValidationCreate(Json::Value params); - Json::Value doValidationSeed(Json::Value params); + Json::Value doValidationCreate(Json::Value params, int& cost); + Json::Value doValidationSeed(Json::Value params, int& cost); - Json::Value doWalletAccounts(Json::Value params); - Json::Value doWalletLock(Json::Value params); - Json::Value doWalletPropose(Json::Value params); - Json::Value doWalletSeed(Json::Value params); - Json::Value doWalletUnlock(Json::Value params); - Json::Value doWalletVerify(Json::Value params); + Json::Value doWalletAccounts(Json::Value params, int& cost); + Json::Value doWalletLock(Json::Value params, int& cost); + Json::Value doWalletPropose(Json::Value params, int& cost); + Json::Value doWalletSeed(Json::Value params, int& cost); + Json::Value doWalletUnlock(Json::Value params, int& cost); + Json::Value doWalletVerify(Json::Value params, int& cost); #if ENABLE_INSECURE - Json::Value doLogin(Json::Value params); + Json::Value doLogin(Json::Value params, int& cost); #endif - Json::Value doLedgerAccept(Json::Value params); - Json::Value doLedgerClosed(Json::Value params); - Json::Value doLedgerCurrent(Json::Value params); - Json::Value doLedgerEntry(Json::Value params); - Json::Value doLedgerHeader(Json::Value params); - Json::Value doTransactionEntry(Json::Value params); + Json::Value doLedgerAccept(Json::Value params, int& cost); + Json::Value doLedgerClosed(Json::Value params, int& cost); + Json::Value doLedgerCurrent(Json::Value params, int& cost); + Json::Value doLedgerEntry(Json::Value params, int& cost); + Json::Value doLedgerHeader(Json::Value params, int& cost); + Json::Value doTransactionEntry(Json::Value params, int& cost); - Json::Value doSubscribe(Json::Value params); - Json::Value doUnsubscribe(Json::Value params); + Json::Value doSubscribe(Json::Value params, int& cost); + Json::Value doUnsubscribe(Json::Value params, int& cost); public: enum { GUEST, USER, ADMIN, FORBID }; - RPCHandler(NetworkOPs* netOps, LoadSource&); - RPCHandler(NetworkOPs* netOps, InfoSub::pointer infoSub, LoadSource&); + RPCHandler(NetworkOPs* netOps); + RPCHandler(NetworkOPs* netOps, InfoSub::pointer infoSub); - Json::Value doCommand(const Json::Value& jvRequest, int role); - Json::Value doRpcCommand(const std::string& strCommand, Json::Value& jvParams, int iRole); + Json::Value doCommand(const Json::Value& jvRequest, int role, int& cost); + Json::Value doRpcCommand(const std::string& strCommand, Json::Value& jvParams, int iRole, int& cost); }; class RPCInternalHandler diff --git a/src/cpp/ripple/RPCServer.cpp b/src/cpp/ripple/RPCServer.cpp index 33185c3b78..f3b81e577d 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), mLoadSource("rpc"), mSocket(io_service) + : mNetOps(nopNetwork), 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, mLoadSource); + mReplyStr = handleRequest(req); 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, LoadSource& ls) +std::string RPCServer::handleRequest(const std::string& requestStr) { cLog(lsTRACE) << "handleRequest " << requestStr; @@ -154,10 +154,11 @@ std::string RPCServer::handleRequest(const std::string& requestStr, LoadSource& return HTTPReply(403, "Forbidden"); } - RPCHandler mRPCHandler(mNetOps, mLoadSource); + RPCHandler mRPCHandler(mNetOps); cLog(lsTRACE) << valParams; - Json::Value result = mRPCHandler.doRpcCommand(strMethod, valParams, mRole); + int cost = 10; + Json::Value result = mRPCHandler.doRpcCommand(strMethod, valParams, mRole, cost); cLog(lsTRACE) << result; std::string strReply = JSONRPCReply(result, Json::Value(), id); diff --git a/src/cpp/ripple/RPCServer.h b/src/cpp/ripple/RPCServer.h index bfc6a4c0e2..44ca235fdd 100644 --- a/src/cpp/ripple/RPCServer.h +++ b/src/cpp/ripple/RPCServer.h @@ -24,7 +24,6 @@ public: private: NetworkOPs* mNetOps; - LoadSource mLoadSource; boost::asio::ip::tcp::socket mSocket; @@ -46,7 +45,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, LoadSource& ls); + std::string handleRequest(const std::string& requestStr); 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 e518739dde..b3137aa646 100644 --- a/src/cpp/ripple/RippleCalc.cpp +++ b/src/cpp/ripple/RippleCalc.cpp @@ -605,7 +605,7 @@ void PathState::setCanonical( && pnPrv.uCurrencyID != pnNxt.uCurrencyID) { // Offer can be implied by currency change. -// XXX What abount issuer? +// XXX What about issuer? bSkip = true; } @@ -934,21 +934,29 @@ TER RippleCalc::calcNodeAdvance( assert(musUnfundedFound.find(uOfferIndex) != musUnfundedFound.end()); // Verify reverse found it too. // Just skip it. It will be deleted. - // bEntryAdvance = true; // Already set continue; } else if (!saTakerPays.isPositive() || !saTakerGets.isPositive()) { // Offer has bad amounts. Offers should never have a bad amounts. - if (musUnfundedFound.find(uOfferIndex) != musUnfundedFound.end()) + if (bReverse) { - // An internal error, offer was found failed to place this in musUnfundedFound. + // Past internal error, offer had bad amounts. cLog(lsWARNING) << boost::str(boost::format("calcNodeAdvance: PAST INTERNAL ERROR: OFFER NON-POSITIVE: saTakerPays=%s saTakerGets=%s") % saTakerPays % saTakerGets); + musUnfundedFound.insert(uOfferIndex); // Mark offer for always deletion. + continue; + } + else if (musUnfundedFound.find(uOfferIndex) != musUnfundedFound.end()) + { + // Past internal error, offer was found failed to place this in musUnfundedFound. + cLog(lsDEBUG) << 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 + continue; } else { @@ -956,15 +964,13 @@ TER RippleCalc::calcNodeAdvance( // 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 @@ -978,8 +984,6 @@ TER RippleCalc::calcNodeAdvance( { // Temporarily unfunded. Another node uses this source, ignore in this offer. cLog(lsTRACE) << "calcNodeAdvance: temporarily unfunded offer (forward)"; - - bEntryAdvance = true; continue; } @@ -993,24 +997,13 @@ TER RippleCalc::calcNodeAdvance( { // Temporarily unfunded. Another node uses this source, ignore in this offer. cLog(lsTRACE) << "calcNodeAdvance: temporarily unfunded offer (reverse)"; - - bEntryAdvance = true; continue; } - 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; - } + // We only need to know if it might need to be marked unfunded. + curIssuerNodeConstIterator itPast = mumSource.find(asLine); + bool bFoundPast = itPast != mumSource.end(); // Only the current node is allowed to use the source. @@ -1029,20 +1022,17 @@ TER RippleCalc::calcNodeAdvance( } else { - // Moving forward, don't need to check again. - // Or source was used this reverse - // Or source was previously used - // XXX + // Moving forward, don't need to insert again + // Or, already found it. } // YYY Could verify offer is correct place for unfundeds. - bEntryAdvance = true; continue; } if (bReverse // Need to remember reverse mention. && !bFoundPast // Not mentioned in previous passes. - && !bFoundReverse) // Not mentioned for pass. + && !bFoundReverse) // New to pass. { // Consider source mentioned by current path state. cLog(lsTRACE) << boost::str(boost::format("calcNodeAdvance: remember=%s/%s/%s") @@ -1324,6 +1314,7 @@ TER RippleCalc::calcNodeDeliverFwd( const uint160& uNxtAccountID = pnNxt.uAccountID; const uint160& uCurCurrencyID = pnCur.uCurrencyID; const uint160& uCurIssuerID = pnCur.uIssuerID; + const uint256& uOfferIndex = pnCur.uOfferIndex; const uint160& uPrvCurrencyID = pnPrv.uCurrencyID; const uint160& uPrvIssuerID = pnPrv.uIssuerID; const STAmount& saInTransRate = pnPrv.saTransferRate; @@ -1343,14 +1334,23 @@ TER RippleCalc::calcNodeDeliverFwd( { if (++loopCount > 40) { - cLog(lsWARNING) << "max loops cndf"; + cLog(lsWARNING) << "calcNodeDeliverFwd: 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. - if (tesSUCCESS == terResult) + if (tesSUCCESS != terResult) + { + nothing(); + } + else if (!uOfferIndex) + { + cLog(lsWARNING) << "calcNodeDeliverFwd: INTERNAL ERROR: Ran out of offers."; + return mOpenLedger ? telFAILED_PROCESSING : tecFAILED_PROCESSING; + } + else if (tesSUCCESS == terResult) { // Doesn't charge input. Input funds are in limbo. bool& bEntryAdvance = pnCur.bEntryAdvance; @@ -1384,9 +1384,11 @@ TER RippleCalc::calcNodeDeliverFwd( STAmount saInPassFees; // Will be determined by adjusted saInPassAct. - cLog(lsINFO) << boost::str(boost::format("calcNodeDeliverFwd: uNode=%d saOutFunded=%s saInReq=%s saInAct=%s saInFees=%s saInFunded=%s saInTotal=%s saInSum=%s saInPassAct=%s saOutPassMax=%s") + cLog(lsINFO) << boost::str(boost::format("calcNodeDeliverFwd: uNode=%d saOutFunded=%s saOfferFunds=%s saTakerGets=%s saInReq=%s saInAct=%s saInFees=%s saInFunded=%s saInTotal=%s saInSum=%s saInPassAct=%s saOutPassMax=%s") % uNode % saOutFunded + % saOfferFunds + % saTakerGets % saInReq % saInAct % saInFees @@ -1396,7 +1398,26 @@ TER RippleCalc::calcNodeDeliverFwd( % saInPassAct % saOutPassMax); - if (!!uNxtAccountID) + if (!saInSum) + { + cLog(lsINFO) << "calcNodeDeliverFwd: Microscopic offer unfunded."; + + // After math offer is effectively unfunded. + psCur.vUnfundedBecame.push_back(uOfferIndex); + bEntryAdvance = true; + continue; + } + else if (!saInFunded) + { + // Previous check should catch this. + cLog(lsWARNING) << "calcNodeDeliverFwd: UNREACHABLE REACHED"; + + // After math offer is effectively unfunded. + psCur.vUnfundedBecame.push_back(uOfferIndex); + bEntryAdvance = true; + continue; + } + else if (!!uNxtAccountID) { // ? --> OFFER --> account // Input fees: vary based upon the consumed offer's owner. @@ -1613,7 +1634,6 @@ TER RippleCalc::calcNodeOfferFwd( // - prv is the driver: it calculates current deliver based on previous delivery limits and current wants. // This routine is called one or two times for a node in a pass. If called once, it will work and set a rate. If called again, // the new work must not worsen the previous rate. -// XXX Deal with uQualityIn or uQualityOut = 0 void RippleCalc::calcNodeRipple( const uint32 uQualityIn, const uint32 uQualityOut, @@ -2189,13 +2209,21 @@ TER RippleCalc::calcNodeAccountFwd( STAmount saIssueCrd = uQualityIn >= QUALITY_ONE ? saPrvIssueReq // No fee. - : STAmount::multiply(saPrvIssueReq, uQualityIn, uCurrencyID, saPrvIssueReq.getIssuer()); // Fee. + : STAmount::multiply(saPrvIssueReq, STAmount(CURRENCY_ONE, ACCOUNT_ONE, uQualityIn, -9)); // Amount to credit. - // Amount to credit. + // Amount to credit. Credit for less than received as a surcharge. saCurReceive = saPrvRedeemReq+saIssueCrd; - // Actually receive. - terResult = lesActive.rippleCredit(uPrvAccountID, uCurAccountID, saPrvRedeemReq+saPrvIssueReq, false); + if (saCurReceive) + { + // Actually receive. + terResult = lesActive.rippleCredit(uPrvAccountID, uCurAccountID, saPrvRedeemReq+saPrvIssueReq, false); + } + else + { + // After applying quality, total payment was microscopic. + terResult = tecPATH_DRY; + } } else { @@ -2237,8 +2265,12 @@ TER RippleCalc::calcNodeAccountFwd( calcNodeRipple(uQualityIn, QUALITY_ONE, saPrvIssueReq, saCurIssueReq, saPrvIssueAct, saCurIssueAct, uRateMax); } + STAmount saProvide = saCurRedeemAct + saCurIssueAct; + // Adjust prv --> cur balance : take all inbound - terResult = lesActive.rippleCredit(uPrvAccountID, uCurAccountID, saPrvRedeemReq + saPrvIssueReq, false); + terResult = saProvide + ? lesActive.rippleCredit(uPrvAccountID, uCurAccountID, saPrvRedeemReq + saPrvIssueReq, false) + : tecPATH_DRY; } } else if (bPrvAccount && !bNxtAccount) @@ -2274,7 +2306,9 @@ TER RippleCalc::calcNodeAccountFwd( } // Adjust prv --> cur balance : take all inbound - terResult = lesActive.rippleCredit(uPrvAccountID, uCurAccountID, saPrvRedeemReq + saPrvIssueReq, false); + terResult = saCurDeliverAct + ? lesActive.rippleCredit(uPrvAccountID, uCurAccountID, saPrvRedeemReq + saPrvIssueReq, false) + : tecPATH_DRY; // Didn't actually deliver anything. } else { @@ -2294,7 +2328,11 @@ TER RippleCalc::calcNodeAccountFwd( } saCurSendMaxPass = saCurDeliverAct; // Record amount sent for pass. - if (!!uCurrencyID) + if (!saCurDeliverAct) + { + terResult = tecPATH_DRY; + } + else if (!!uCurrencyID) { // Non-XRP, current node is the issuer. // We could be delivering to multiple accounts, so we don't know which ripple balance will be adjusted. Assume @@ -2355,6 +2393,10 @@ TER RippleCalc::calcNodeAccountFwd( } // No income balance adjustments necessary. The paying side inside the offer paid and the next link will receive. + STAmount saProvide = saCurRedeemAct + saCurIssueAct; + + if (!saProvide) + terResult = tecPATH_DRY; } } else @@ -2373,6 +2415,8 @@ TER RippleCalc::calcNodeAccountFwd( } // No income balance adjustments necessary. The paying side inside the offer paid and the next link will receive. + if (!saCurDeliverAct) + terResult = tecPATH_DRY; } return terResult; diff --git a/src/cpp/ripple/WSConnection.h b/src/cpp/ripple/WSConnection.h index 7ce02fba4c..25a7fcfe3e 100644 --- a/src/cpp/ripple/WSConnection.h +++ b/src/cpp/ripple/WSConnection.h @@ -88,6 +88,14 @@ public: // Utilities Json::Value invokeCommand(Json::Value& jvRequest) { + if (theApp->getLoadManager().shouldCutoff(mLoadSource)) + { + connection_ptr ptr = mConnection.lock(); + if (ptr) + ptr->close(websocketpp::close::status::PROTOCOL_ERROR, "overload"); + return rpcError(rpcTOO_BUSY); + } + if (!jvRequest.isMember("command")) { Json::Value jvResult(Json::objectValue); @@ -102,11 +110,12 @@ public: jvResult["id"] = jvRequest["id"]; } + theApp->getLoadManager().adjust(mLoadSource, 5); return jvResult; } - RPCHandler mRPCHandler(&mNetwork, - boost::shared_polymorphic_downcast(this->shared_from_this()), mLoadSource); + int cost = 10; + RPCHandler mRPCHandler(&mNetwork, boost::shared_polymorphic_downcast(this->shared_from_this())); Json::Value jvResult(Json::objectValue); int iRole = mHandler->getPublic() @@ -119,9 +128,12 @@ public: } else { - jvResult["result"] = mRPCHandler.doCommand(jvRequest, iRole); + jvResult["result"] = mRPCHandler.doCommand(jvRequest, iRole, cost); } + if (theApp->getLoadManager().adjust(mLoadSource, cost) && theApp->getLoadManager().shouldWarn(mLoadSource)) + jvResult["warning"] = "load"; + // Currently we will simply unwrap errors returned by the RPC // API, in the future maybe we can make the responses // consistent. diff --git a/src/cpp/ripple/main.cpp b/src/cpp/ripple/main.cpp index 1c114aa075..4493786f92 100644 --- a/src/cpp/ripple/main.cpp +++ b/src/cpp/ripple/main.cpp @@ -43,10 +43,10 @@ void startServer() if (!theConfig.QUIET) std::cerr << "Startup RPC: " << jvCommand << std::endl; - LoadSource ls(true); - RPCHandler rhHandler(&theApp->getOPs(), ls); + RPCHandler rhHandler(&theApp->getOPs()); - Json::Value jvResult = rhHandler.doCommand(jvCommand, RPCHandler::ADMIN); + int cost = 10; + Json::Value jvResult = rhHandler.doCommand(jvCommand, RPCHandler::ADMIN, cost); if (!theConfig.QUIET) std::cerr << "Result: " << jvResult << std::endl;