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/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;