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/database/SqliteDatabase.cpp b/src/cpp/database/SqliteDatabase.cpp index 1033dcf4e..7db0a623f 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/Application.cpp b/src/cpp/ripple/Application.cpp index cc417257c..f0c360265 100644 --- a/src/cpp/ripple/Application.cpp +++ b/src/cpp/ripple/Application.cpp @@ -26,9 +26,9 @@ Application* theApp = NULL; DatabaseCon::DatabaseCon(const std::string& strName, const char *initStrings[], int initCount) { - boost::filesystem::path pPath = theConfig.RUN_STANDALONE && !Config::LOAD + boost::filesystem::path pPath = (theConfig.RUN_STANDALONE && (!theConfig.START_UP != Config::LOAD)) ? "" // Use temporary files. - : theConfig.DATA_DIR / strName; // Use regular db files. + : (theConfig.DATA_DIR / strName); // Use regular db files. mDatabase = new SqliteDatabase(pPath.string().c_str()); mDatabase->connect(); @@ -45,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/LoadManager.cpp b/src/cpp/ripple/LoadManager.cpp index 87b2237a4..1eaad6c6b 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 55796ea02..070384d70 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; } + const std::string& getName() { return mName; } bool isPrivileged() const { return (mFlags & lsfPrivileged) != 0; } void setPrivileged() { mFlags |= lsfPrivileged; } @@ -106,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(); @@ -124,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/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 43916603f..76d135017 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,15 +67,11 @@ int iAdminGet(const Json::Value& jvRequest, const std::string& strRemoteIp) return iRole; } -RPCHandler::RPCHandler(NetworkOPs* netOps) -{ - mNetOps = netOps; -} +RPCHandler::RPCHandler(NetworkOPs* netOps) : mNetOps(netOps) +{ ; } -RPCHandler::RPCHandler(NetworkOPs* netOps, InfoSub::pointer infoSub) : mInfoSub(infoSub) -{ - mNetOps = netOps; -} +RPCHandler::RPCHandler(NetworkOPs* netOps, InfoSub::pointer infoSub) : mNetOps(netOps), mInfoSub(infoSub) +{ ; } Json::Value RPCHandler::transactionSign(Json::Value jvRequest, bool bSubmit) { @@ -565,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); @@ -610,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"; @@ -631,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); @@ -657,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); @@ -680,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")) @@ -740,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); @@ -765,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); @@ -774,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); } @@ -784,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(); @@ -877,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); @@ -956,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); @@ -1027,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) { @@ -1124,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; @@ -1150,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; @@ -1230,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); @@ -1363,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); } @@ -1372,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")) { @@ -1387,6 +1389,7 @@ Json::Value RPCHandler::doSubmit(Json::Value jvRequest) { return rpcError(rpcINVALID_PARAMS); } + cost = rpcCOST_EXPENSIVE; Serializer sTrans(vucBlob); SerializerIterator sitTrans(sTrans); @@ -1458,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); @@ -1467,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); @@ -1476,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); @@ -1488,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); @@ -1522,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); @@ -1582,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; @@ -1595,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; @@ -1609,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")) { @@ -1655,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; @@ -1752,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); @@ -1777,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")) @@ -1844,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); @@ -1887,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(); } @@ -1895,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; @@ -1925,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"); @@ -1964,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")) @@ -2000,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; @@ -2022,7 +2025,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; @@ -2030,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")) @@ -2078,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() : ""; @@ -2102,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); @@ -2125,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); @@ -2135,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)) { @@ -2147,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(); @@ -2155,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(); @@ -2163,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; @@ -2200,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); @@ -2327,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); @@ -2534,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); @@ -2584,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); @@ -2732,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); @@ -2845,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) @@ -2860,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")) @@ -2875,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); @@ -3002,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()) @@ -3023,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 48fe696ce..641687ccf 100644 --- a/src/cpp/ripple/RPCHandler.h +++ b/src/cpp/ripple/RPCHandler.h @@ -19,7 +19,7 @@ class RPCHandler InfoSub::pointer mInfoSub; int mRole; - 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 @@ -43,71 +43,71 @@ 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: @@ -116,8 +116,8 @@ public: 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 cd7474261..f3b81e577 100644 --- a/src/cpp/ripple/RPCServer.cpp +++ b/src/cpp/ripple/RPCServer.cpp @@ -157,7 +157,8 @@ std::string RPCServer::handleRequest(const std::string& requestStr) 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 c96774354..44ca235fd 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 { diff --git a/src/cpp/ripple/RippleCalc.cpp b/src/cpp/ripple/RippleCalc.cpp index 2f7096cbb..b3137aa64 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. { @@ -491,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 @@ -531,8 +531,7 @@ void PathState::setCanonical( } else { - // Node 1 must be an account - + // Node 1 must be an account } } else @@ -2983,7 +2982,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? @@ -3022,26 +3021,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 @@ -3095,13 +3094,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/WSConnection.h b/src/cpp/ripple/WSConnection.h index 399c6a827..25a7fcfe3 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(); } @@ -86,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); @@ -100,9 +110,11 @@ public: jvResult["id"] = jvRequest["id"]; } + theApp->getLoadManager().adjust(mLoadSource, 5); return jvResult; } + int cost = 10; RPCHandler mRPCHandler(&mNetwork, boost::shared_polymorphic_downcast(this->shared_from_this())); Json::Value jvResult(Json::objectValue); @@ -116,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 88dc35a33..4493786f9 100644 --- a/src/cpp/ripple/main.cpp +++ b/src/cpp/ripple/main.cpp @@ -45,7 +45,8 @@ void startServer() 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; 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