From 0bcd2eb09a195f24e041416d734ea2e7b12b2352 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Wed, 29 May 2013 11:54:20 -0700 Subject: [PATCH 01/17] Static feature support. --- src/cpp/ripple/Application.cpp | 2 ++ src/cpp/ripple/FeatureTable.cpp | 6 +++--- src/cpp/ripple/FeatureTable.h | 34 ++++++++++++++++++++------------- 3 files changed, 26 insertions(+), 16 deletions(-) diff --git a/src/cpp/ripple/Application.cpp b/src/cpp/ripple/Application.cpp index fcd0a6d07a..9c5bc7712c 100644 --- a/src/cpp/ripple/Application.cpp +++ b/src/cpp/ripple/Application.cpp @@ -431,6 +431,8 @@ void Application::startNewLedger() { Ledger::pointer firstLedger = boost::make_shared(rootAddress, SYSTEM_CURRENCY_START); assert(!!firstLedger->getAccountState(rootAddress)); + // WRITEME: Add any default features + // WRITEME: Set default fee/reserve firstLedger->updateHash(); firstLedger->setClosed(); firstLedger->setAccepted(); diff --git a/src/cpp/ripple/FeatureTable.cpp b/src/cpp/ripple/FeatureTable.cpp index e76e7cb152..eff8a8be24 100644 --- a/src/cpp/ripple/FeatureTable.cpp +++ b/src/cpp/ripple/FeatureTable.cpp @@ -3,11 +3,11 @@ SETUP_LOG (FeatureTable) void FeatureTable::addInitialFeatures() { - // For each feature this version supports, call enableFeature. - // Permanent vetos can also be added here. + // For each feature this version supports, construct the FeatureState object by calling + // getCreateFeature. Set any vetoes or defaults. A pointer to the FeatureState can be stashed } -FeatureTable::FeatureState* FeatureTable::getCreateFeature(const uint256& featureHash, bool create) +FeatureState* FeatureTable::getCreateFeature(const uint256& featureHash, bool create) { // call with the mutex held featureMap_t::iterator it = mFeatureMap.find(featureHash); if (it == mFeatureMap.end()) diff --git a/src/cpp/ripple/FeatureTable.h b/src/cpp/ripple/FeatureTable.h index 9579f40195..79c9f1f654 100644 --- a/src/cpp/ripple/FeatureTable.h +++ b/src/cpp/ripple/FeatureTable.h @@ -18,23 +18,31 @@ public: void addVote(const uint256& feature) { ++mVotes[feature]; } }; +class FeatureState +{ +public: + uint256 mHash; // Feature hash + bool mVetoed; // We don't want this feature enabled + bool mEnabled; + bool mSupported; + bool mDefault; // Include in genesis ledger + + uint32 mFirstMajority; // First time we saw a majority (close time) + uint32 mLastMajority; // Most recent time we saw a majority (close time) + + FeatureState() + : mVetoed(false), mEnabled(false), mSupported(false), mDefault(false), + mFirstMajority(0), mLastMajority(0) { ; } + + void setVeto() { mVetoed = true; } + void setDefault() { mDefault = true; } +}; + + class FeatureTable { protected: - class FeatureState - { - public: - bool mVetoed; // We don't want this feature enabled - bool mEnabled; - bool mSupported; - - uint32 mFirstMajority; // First time we saw a majority (close time) - uint32 mLastMajority; // Most recent time we saw a majority (close time) - - FeatureState() : mVetoed(false), mEnabled(false), mSupported(false), mFirstMajority(0), mLastMajority(0) { ; } - }; - typedef boost::unordered_map featureMap_t; typedef std::pair featureIt_t; typedef boost::unordered_set featureList_t; From 8781e131c5787f2ea32f0f3640fca0d9158fdd28 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Wed, 29 May 2013 13:04:05 -0700 Subject: [PATCH 02/17] Write back feature majority times. Mark a FIXME. --- src/cpp/ripple/FeatureTable.cpp | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/src/cpp/ripple/FeatureTable.cpp b/src/cpp/ripple/FeatureTable.cpp index eff8a8be24..d787f83263 100644 --- a/src/cpp/ripple/FeatureTable.cpp +++ b/src/cpp/ripple/FeatureTable.cpp @@ -193,7 +193,22 @@ void FeatureTable::reportValidations(const FeatureSet& set) if (!changedFeatures.empty()) { - // WRITEME write changed features to SQL db + ScopedLock sl(theApp->getWalletDB()->getDBLock()); + Database* db = theApp->getWalletDB()->getDB(); + + db->executeSQL("BEGIN TRANSACTION;"); + BOOST_FOREACH(const uint256& hash, changedFeatures) + { + FeatureState& fState = mFeatureMap[hash]; + db->executeSQL(boost::str(boost::format( + "UPDATE Features SET FirstMajority = %d WHERE Hash = '%s';" + ) % fState.mFirstMajority % hash.GetHex())); + db->executeSQL(boost::str(boost::format( + "UPDATE Features SET LastMajority = %d WHERE Hash = '%s';" + ) % fState.mLastMajority % hash.GetHex())); + } + db->executeSQL("END TRANSACTION;"); + changedFeatures.clear(); } } @@ -246,12 +261,12 @@ void FeatureTable::doVoting(Ledger::ref lastClosedLedger, SHAMap::ref initialPos BOOST_FOREACH(const uint256& uFeature, lFeatures) { - WriteLog (lsWARNING, FeatureTable) << "We are voting for feature " << uFeature; + WriteLog (lsWARNING, FeatureTable) << "Voting for feature: " << uFeature; SerializedTransaction trans(ttFEATURE); trans.setFieldAccount(sfAccount, uint160()); trans.setFieldH256(sfFeature, uFeature); uint256 txID = trans.getTransactionID(); - WriteLog (lsWARNING, FeatureTable) << "Vote: " << txID; + WriteLog (lsWARNING, FeatureTable) << "Vote ID: " << txID; Serializer s; trans.add(s, true); @@ -271,7 +286,7 @@ Json::Value FeatureTable::getJson(int) boost::mutex::scoped_lock sl(mMutex); BOOST_FOREACH(const featureIt_t& it, mFeatureMap) { - Json::Value v(Json::objectValue); + Json::Value& v(ret[it.first.GetHex()] = Json::objectValue); v["supported"] = it.second.mSupported; @@ -283,7 +298,9 @@ Json::Value FeatureTable::getJson(int) if (mLastReport != 0) { if (it.second.mLastMajority == 0) + { v["majority"] = false; + } else { if (it.second.mFirstMajority != 0) @@ -306,8 +323,6 @@ Json::Value FeatureTable::getJson(int) if (it.second.mVetoed) v["veto"] = true; - - ret[it.first.GetHex()] = v; } } @@ -350,6 +365,7 @@ public: typedef typename std::map::value_type mapVType; BOOST_FOREACH(const mapVType& value, mVoteMap) { // Take most voted value between current and target, inclusive + // FIXME: Should take best value that can get a significant majority if ((value.first <= std::max(mTarget, mCurrent)) && (value.first >= std::min(mTarget, mCurrent)) && (value.second > weight)) From f716fe2cab07b27d60781d4f0180d6536c9da1b6 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Wed, 29 May 2013 16:18:56 -0700 Subject: [PATCH 03/17] RPC infrastructure for feature management. --- src/cpp/ripple/CallRPC.cpp | 15 +++++++++++++++ src/cpp/ripple/CallRPC.h | 1 + src/cpp/ripple/FeatureTable.cpp | 4 ++++ src/cpp/ripple/RPCHandler.cpp | 19 +++++++++++++++++++ src/cpp/ripple/RPCHandler.h | 1 + 5 files changed, 40 insertions(+) diff --git a/src/cpp/ripple/CallRPC.cpp b/src/cpp/ripple/CallRPC.cpp index 3193e36832..27a7e01445 100644 --- a/src/cpp/ripple/CallRPC.cpp +++ b/src/cpp/ripple/CallRPC.cpp @@ -311,6 +311,20 @@ Json::Value RPCParser::parseEvented(const Json::Value& jvParams) return rpcError(rpcNO_EVENTS); } +// feature [] [true|false] +Json::Value RPCParser::parseFeature(const Json::Value& jvParams) +{ + Json::Value jvRequest(Json::objectValue); + + if (jvParams.size() > 0) + jvRequest["feature"] = jvParams[0u].asString(); + + if (jvParams.size() > 1) + jvRequest["vote"] = boost::lexical_cast(jvParams[1u].asString()); + + return jvRequest; +} + // get_counts [] Json::Value RPCParser::parseGetCounts(const Json::Value& jvParams) { @@ -734,6 +748,7 @@ Json::Value RPCParser::parseCommand(std::string strMethod, Json::Value jvParams) { "book_offers", &RPCParser::parseBookOffers, 2, 7 }, { "connect", &RPCParser::parseConnect, 1, 2 }, { "consensus_info", &RPCParser::parseAsIs, 0, 0 }, + { "feature", &RPCParser::parseFeature, 0, 2 }, { "get_counts", &RPCParser::parseGetCounts, 0, 1 }, { "json", &RPCParser::parseJson, 2, 2 }, { "ledger", &RPCParser::parseLedger, 0, 2 }, diff --git a/src/cpp/ripple/CallRPC.h b/src/cpp/ripple/CallRPC.h index 3ce69167a6..0ce9a86303 100644 --- a/src/cpp/ripple/CallRPC.h +++ b/src/cpp/ripple/CallRPC.h @@ -22,6 +22,7 @@ protected: Json::Value parseDataStore(const Json::Value& jvParams); #endif Json::Value parseEvented(const Json::Value& jvParams); + Json::Value parseFeature(const Json::Value& jvParams); Json::Value parseGetCounts(const Json::Value& jvParams); Json::Value parseInternal(const Json::Value& jvParams); Json::Value parseJson(const Json::Value& jvParams); diff --git a/src/cpp/ripple/FeatureTable.cpp b/src/cpp/ripple/FeatureTable.cpp index d787f83263..e93a5821b4 100644 --- a/src/cpp/ripple/FeatureTable.cpp +++ b/src/cpp/ripple/FeatureTable.cpp @@ -1,10 +1,14 @@ SETUP_LOG (FeatureTable) +FeatureState* testFeature = NULL; + void FeatureTable::addInitialFeatures() { // For each feature this version supports, construct the FeatureState object by calling // getCreateFeature. Set any vetoes or defaults. A pointer to the FeatureState can be stashed + + testFeature = getCreateFeature(uint256("1234"), true); } FeatureState* FeatureTable::getCreateFeature(const uint256& featureHash, bool create) diff --git a/src/cpp/ripple/RPCHandler.cpp b/src/cpp/ripple/RPCHandler.cpp index 49755949e8..d79232affb 100644 --- a/src/cpp/ripple/RPCHandler.cpp +++ b/src/cpp/ripple/RPCHandler.cpp @@ -2265,6 +2265,24 @@ static void textTime(std::string& text, int& seconds, const char *unitName, int text += "s"; } +Json::Value RPCHandler::doFeature(Json::Value jvRequest, int& cost, ScopedLock& mlh) +{ + if (!jvRequest.isMember("feature")) + { + Json::Value jvReply = Json::objectValue; + jvReply["features"] = theApp->getFeatureTable().getJson(0); + return jvReply; + } + + if (!jvRequest.isMember("vote")) + { + // WRITEME + } + + // WRITEME + return rpcError(rpcNOT_SUPPORTED); +} + // { // min_count: // optional, defaults to 10 // } @@ -3471,6 +3489,7 @@ Json::Value RPCHandler::doCommand(const Json::Value& jvRequest, int iRole, int & { "consensus_info", &RPCHandler::doConsensusInfo, true, optNone }, { "get_counts", &RPCHandler::doGetCounts, true, optNone }, { "internal", &RPCHandler::doInternal, true, optNone }, + { "feature", &RPCHandler::doFeature, true, optNone }, { "ledger", &RPCHandler::doLedger, false, optNetwork }, { "ledger_accept", &RPCHandler::doLedgerAccept, true, optCurrent }, { "ledger_closed", &RPCHandler::doLedgerClosed, false, optClosed }, diff --git a/src/cpp/ripple/RPCHandler.h b/src/cpp/ripple/RPCHandler.h index ed3fd12a21..849aaf0eb3 100644 --- a/src/cpp/ripple/RPCHandler.h +++ b/src/cpp/ripple/RPCHandler.h @@ -59,6 +59,7 @@ class RPCHandler Json::Value doDataFetch(Json::Value params, int& cost, ScopedLock& mlh); Json::Value doDataStore(Json::Value params, int& cost, ScopedLock& mlh); #endif + Json::Value doFeature(Json::Value params, int& cost, ScopedLock& mlh); Json::Value doGetCounts(Json::Value params, int& cost, ScopedLock& mlh); Json::Value doInternal(Json::Value params, int& cost, ScopedLock& mlh); Json::Value doLedger(Json::Value params, int& cost, ScopedLock& mlh); From f3b04e666110405f3292491a45383f4288e99df8 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Wed, 29 May 2013 16:46:28 -0700 Subject: [PATCH 04/17] Bugfix. --- src/cpp/ripple/Application.cpp | 2 ++ src/cpp/ripple/FeatureTable.h | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/cpp/ripple/Application.cpp b/src/cpp/ripple/Application.cpp index 6e2140e95e..c1ccf84c4c 100644 --- a/src/cpp/ripple/Application.cpp +++ b/src/cpp/ripple/Application.cpp @@ -201,6 +201,8 @@ void Application::setup() if (!theConfig.RUN_STANDALONE) updateTables(theConfig.LDB_IMPORT); + mFeatureTable.addInitialFeatures(); + if (theConfig.START_UP == Config::FRESH) { WriteLog (lsINFO, Application) << "Starting new Ledger"; diff --git a/src/cpp/ripple/FeatureTable.h b/src/cpp/ripple/FeatureTable.h index 79c9f1f654..aecc60b19b 100644 --- a/src/cpp/ripple/FeatureTable.h +++ b/src/cpp/ripple/FeatureTable.h @@ -61,7 +61,7 @@ public: FeatureTable(uint32 majorityTime, int majorityFraction) : mMajorityTime(majorityTime), mMajorityFraction(majorityFraction), mFirstReport(0), mLastReport(0) - { addInitialFeatures(); } + { ; } void addInitialFeatures(); From b66e06369086e287582910d889e8e4a10dbfbb1a Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Thu, 30 May 2013 10:09:20 -0700 Subject: [PATCH 05/17] Updates --- src/cpp/ripple/FeatureTable.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/cpp/ripple/FeatureTable.h b/src/cpp/ripple/FeatureTable.h index aecc60b19b..b915232113 100644 --- a/src/cpp/ripple/FeatureTable.h +++ b/src/cpp/ripple/FeatureTable.h @@ -36,6 +36,10 @@ public: void setVeto() { mVetoed = true; } void setDefault() { mDefault = true; } + bool isDefault() { return mDefault; } + bool isSupported() { return mSupported; } + bool isVetoed() { return mVetoed; } + bool isEnabled() { return mEnabled; } }; From 76358edc310d07a1ddcc0fc337e9a4cfbb864497 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Wed, 29 May 2013 11:54:20 -0700 Subject: [PATCH 06/17] Static feature support. --- src/cpp/ripple/Application.cpp | 2 ++ src/cpp/ripple/FeatureTable.cpp | 6 +++--- src/cpp/ripple/FeatureTable.h | 34 ++++++++++++++++++++------------- 3 files changed, 26 insertions(+), 16 deletions(-) diff --git a/src/cpp/ripple/Application.cpp b/src/cpp/ripple/Application.cpp index a2dcdf4697..a6d23d3b59 100644 --- a/src/cpp/ripple/Application.cpp +++ b/src/cpp/ripple/Application.cpp @@ -429,6 +429,8 @@ void Application::startNewLedger() { Ledger::pointer firstLedger = boost::make_shared(rootAddress, SYSTEM_CURRENCY_START); assert(!!firstLedger->getAccountState(rootAddress)); + // WRITEME: Add any default features + // WRITEME: Set default fee/reserve firstLedger->updateHash(); firstLedger->setClosed(); firstLedger->setAccepted(); diff --git a/src/cpp/ripple/FeatureTable.cpp b/src/cpp/ripple/FeatureTable.cpp index e76e7cb152..eff8a8be24 100644 --- a/src/cpp/ripple/FeatureTable.cpp +++ b/src/cpp/ripple/FeatureTable.cpp @@ -3,11 +3,11 @@ SETUP_LOG (FeatureTable) void FeatureTable::addInitialFeatures() { - // For each feature this version supports, call enableFeature. - // Permanent vetos can also be added here. + // For each feature this version supports, construct the FeatureState object by calling + // getCreateFeature. Set any vetoes or defaults. A pointer to the FeatureState can be stashed } -FeatureTable::FeatureState* FeatureTable::getCreateFeature(const uint256& featureHash, bool create) +FeatureState* FeatureTable::getCreateFeature(const uint256& featureHash, bool create) { // call with the mutex held featureMap_t::iterator it = mFeatureMap.find(featureHash); if (it == mFeatureMap.end()) diff --git a/src/cpp/ripple/FeatureTable.h b/src/cpp/ripple/FeatureTable.h index 9579f40195..79c9f1f654 100644 --- a/src/cpp/ripple/FeatureTable.h +++ b/src/cpp/ripple/FeatureTable.h @@ -18,23 +18,31 @@ public: void addVote(const uint256& feature) { ++mVotes[feature]; } }; +class FeatureState +{ +public: + uint256 mHash; // Feature hash + bool mVetoed; // We don't want this feature enabled + bool mEnabled; + bool mSupported; + bool mDefault; // Include in genesis ledger + + uint32 mFirstMajority; // First time we saw a majority (close time) + uint32 mLastMajority; // Most recent time we saw a majority (close time) + + FeatureState() + : mVetoed(false), mEnabled(false), mSupported(false), mDefault(false), + mFirstMajority(0), mLastMajority(0) { ; } + + void setVeto() { mVetoed = true; } + void setDefault() { mDefault = true; } +}; + + class FeatureTable { protected: - class FeatureState - { - public: - bool mVetoed; // We don't want this feature enabled - bool mEnabled; - bool mSupported; - - uint32 mFirstMajority; // First time we saw a majority (close time) - uint32 mLastMajority; // Most recent time we saw a majority (close time) - - FeatureState() : mVetoed(false), mEnabled(false), mSupported(false), mFirstMajority(0), mLastMajority(0) { ; } - }; - typedef boost::unordered_map featureMap_t; typedef std::pair featureIt_t; typedef boost::unordered_set featureList_t; From d5b1970ab3960e33125996db4a1201063ac7c72f Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Wed, 29 May 2013 13:04:05 -0700 Subject: [PATCH 07/17] Write back feature majority times. Mark a FIXME. --- src/cpp/ripple/FeatureTable.cpp | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/src/cpp/ripple/FeatureTable.cpp b/src/cpp/ripple/FeatureTable.cpp index eff8a8be24..d787f83263 100644 --- a/src/cpp/ripple/FeatureTable.cpp +++ b/src/cpp/ripple/FeatureTable.cpp @@ -193,7 +193,22 @@ void FeatureTable::reportValidations(const FeatureSet& set) if (!changedFeatures.empty()) { - // WRITEME write changed features to SQL db + ScopedLock sl(theApp->getWalletDB()->getDBLock()); + Database* db = theApp->getWalletDB()->getDB(); + + db->executeSQL("BEGIN TRANSACTION;"); + BOOST_FOREACH(const uint256& hash, changedFeatures) + { + FeatureState& fState = mFeatureMap[hash]; + db->executeSQL(boost::str(boost::format( + "UPDATE Features SET FirstMajority = %d WHERE Hash = '%s';" + ) % fState.mFirstMajority % hash.GetHex())); + db->executeSQL(boost::str(boost::format( + "UPDATE Features SET LastMajority = %d WHERE Hash = '%s';" + ) % fState.mLastMajority % hash.GetHex())); + } + db->executeSQL("END TRANSACTION;"); + changedFeatures.clear(); } } @@ -246,12 +261,12 @@ void FeatureTable::doVoting(Ledger::ref lastClosedLedger, SHAMap::ref initialPos BOOST_FOREACH(const uint256& uFeature, lFeatures) { - WriteLog (lsWARNING, FeatureTable) << "We are voting for feature " << uFeature; + WriteLog (lsWARNING, FeatureTable) << "Voting for feature: " << uFeature; SerializedTransaction trans(ttFEATURE); trans.setFieldAccount(sfAccount, uint160()); trans.setFieldH256(sfFeature, uFeature); uint256 txID = trans.getTransactionID(); - WriteLog (lsWARNING, FeatureTable) << "Vote: " << txID; + WriteLog (lsWARNING, FeatureTable) << "Vote ID: " << txID; Serializer s; trans.add(s, true); @@ -271,7 +286,7 @@ Json::Value FeatureTable::getJson(int) boost::mutex::scoped_lock sl(mMutex); BOOST_FOREACH(const featureIt_t& it, mFeatureMap) { - Json::Value v(Json::objectValue); + Json::Value& v(ret[it.first.GetHex()] = Json::objectValue); v["supported"] = it.second.mSupported; @@ -283,7 +298,9 @@ Json::Value FeatureTable::getJson(int) if (mLastReport != 0) { if (it.second.mLastMajority == 0) + { v["majority"] = false; + } else { if (it.second.mFirstMajority != 0) @@ -306,8 +323,6 @@ Json::Value FeatureTable::getJson(int) if (it.second.mVetoed) v["veto"] = true; - - ret[it.first.GetHex()] = v; } } @@ -350,6 +365,7 @@ public: typedef typename std::map::value_type mapVType; BOOST_FOREACH(const mapVType& value, mVoteMap) { // Take most voted value between current and target, inclusive + // FIXME: Should take best value that can get a significant majority if ((value.first <= std::max(mTarget, mCurrent)) && (value.first >= std::min(mTarget, mCurrent)) && (value.second > weight)) From 5af467310454772ae433df63d402ecd28f67311a Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Wed, 29 May 2013 16:18:56 -0700 Subject: [PATCH 08/17] RPC infrastructure for feature management. --- src/cpp/ripple/CallRPC.cpp | 15 +++++++++++++++ src/cpp/ripple/CallRPC.h | 1 + src/cpp/ripple/FeatureTable.cpp | 4 ++++ src/cpp/ripple/RPCHandler.cpp | 19 +++++++++++++++++++ src/cpp/ripple/RPCHandler.h | 1 + 5 files changed, 40 insertions(+) diff --git a/src/cpp/ripple/CallRPC.cpp b/src/cpp/ripple/CallRPC.cpp index 89f2ed4e20..6a6ee9f3a9 100644 --- a/src/cpp/ripple/CallRPC.cpp +++ b/src/cpp/ripple/CallRPC.cpp @@ -310,6 +310,20 @@ Json::Value RPCParser::parseEvented(const Json::Value& jvParams) return rpcError(rpcNO_EVENTS); } +// feature [] [true|false] +Json::Value RPCParser::parseFeature(const Json::Value& jvParams) +{ + Json::Value jvRequest(Json::objectValue); + + if (jvParams.size() > 0) + jvRequest["feature"] = jvParams[0u].asString(); + + if (jvParams.size() > 1) + jvRequest["vote"] = boost::lexical_cast(jvParams[1u].asString()); + + return jvRequest; +} + // get_counts [] Json::Value RPCParser::parseGetCounts(const Json::Value& jvParams) { @@ -733,6 +747,7 @@ Json::Value RPCParser::parseCommand(std::string strMethod, Json::Value jvParams) { "book_offers", &RPCParser::parseBookOffers, 2, 7 }, { "connect", &RPCParser::parseConnect, 1, 2 }, { "consensus_info", &RPCParser::parseAsIs, 0, 0 }, + { "feature", &RPCParser::parseFeature, 0, 2 }, { "get_counts", &RPCParser::parseGetCounts, 0, 1 }, { "json", &RPCParser::parseJson, 2, 2 }, { "ledger", &RPCParser::parseLedger, 0, 2 }, diff --git a/src/cpp/ripple/CallRPC.h b/src/cpp/ripple/CallRPC.h index 3ce69167a6..0ce9a86303 100644 --- a/src/cpp/ripple/CallRPC.h +++ b/src/cpp/ripple/CallRPC.h @@ -22,6 +22,7 @@ protected: Json::Value parseDataStore(const Json::Value& jvParams); #endif Json::Value parseEvented(const Json::Value& jvParams); + Json::Value parseFeature(const Json::Value& jvParams); Json::Value parseGetCounts(const Json::Value& jvParams); Json::Value parseInternal(const Json::Value& jvParams); Json::Value parseJson(const Json::Value& jvParams); diff --git a/src/cpp/ripple/FeatureTable.cpp b/src/cpp/ripple/FeatureTable.cpp index d787f83263..e93a5821b4 100644 --- a/src/cpp/ripple/FeatureTable.cpp +++ b/src/cpp/ripple/FeatureTable.cpp @@ -1,10 +1,14 @@ SETUP_LOG (FeatureTable) +FeatureState* testFeature = NULL; + void FeatureTable::addInitialFeatures() { // For each feature this version supports, construct the FeatureState object by calling // getCreateFeature. Set any vetoes or defaults. A pointer to the FeatureState can be stashed + + testFeature = getCreateFeature(uint256("1234"), true); } FeatureState* FeatureTable::getCreateFeature(const uint256& featureHash, bool create) diff --git a/src/cpp/ripple/RPCHandler.cpp b/src/cpp/ripple/RPCHandler.cpp index 398a702a5f..2f3f0d9661 100644 --- a/src/cpp/ripple/RPCHandler.cpp +++ b/src/cpp/ripple/RPCHandler.cpp @@ -2263,6 +2263,24 @@ static void textTime(std::string& text, int& seconds, const char *unitName, int text += "s"; } +Json::Value RPCHandler::doFeature(Json::Value jvRequest, int& cost, ScopedLock& mlh) +{ + if (!jvRequest.isMember("feature")) + { + Json::Value jvReply = Json::objectValue; + jvReply["features"] = theApp->getFeatureTable().getJson(0); + return jvReply; + } + + if (!jvRequest.isMember("vote")) + { + // WRITEME + } + + // WRITEME + return rpcError(rpcNOT_SUPPORTED); +} + // { // min_count: // optional, defaults to 10 // } @@ -3469,6 +3487,7 @@ Json::Value RPCHandler::doCommand(const Json::Value& jvRequest, int iRole, int & { "consensus_info", &RPCHandler::doConsensusInfo, true, optNone }, { "get_counts", &RPCHandler::doGetCounts, true, optNone }, { "internal", &RPCHandler::doInternal, true, optNone }, + { "feature", &RPCHandler::doFeature, true, optNone }, { "ledger", &RPCHandler::doLedger, false, optNetwork }, { "ledger_accept", &RPCHandler::doLedgerAccept, true, optCurrent }, { "ledger_closed", &RPCHandler::doLedgerClosed, false, optClosed }, diff --git a/src/cpp/ripple/RPCHandler.h b/src/cpp/ripple/RPCHandler.h index 298d76c605..14df015a98 100644 --- a/src/cpp/ripple/RPCHandler.h +++ b/src/cpp/ripple/RPCHandler.h @@ -57,6 +57,7 @@ class RPCHandler Json::Value doDataFetch(Json::Value params, int& cost, ScopedLock& mlh); Json::Value doDataStore(Json::Value params, int& cost, ScopedLock& mlh); #endif + Json::Value doFeature(Json::Value params, int& cost, ScopedLock& mlh); Json::Value doGetCounts(Json::Value params, int& cost, ScopedLock& mlh); Json::Value doInternal(Json::Value params, int& cost, ScopedLock& mlh); Json::Value doLedger(Json::Value params, int& cost, ScopedLock& mlh); From df874d6193ab215b424fb705f698aa3a3bcc40ae Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Wed, 29 May 2013 16:46:28 -0700 Subject: [PATCH 09/17] Bugfix. --- src/cpp/ripple/Application.cpp | 2 ++ src/cpp/ripple/FeatureTable.h | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/cpp/ripple/Application.cpp b/src/cpp/ripple/Application.cpp index a6d23d3b59..b399fcc154 100644 --- a/src/cpp/ripple/Application.cpp +++ b/src/cpp/ripple/Application.cpp @@ -199,6 +199,8 @@ void Application::setup() if (!theConfig.RUN_STANDALONE) updateTables(theConfig.LDB_IMPORT); + mFeatureTable.addInitialFeatures(); + if (theConfig.START_UP == Config::FRESH) { WriteLog (lsINFO, Application) << "Starting new Ledger"; diff --git a/src/cpp/ripple/FeatureTable.h b/src/cpp/ripple/FeatureTable.h index 79c9f1f654..aecc60b19b 100644 --- a/src/cpp/ripple/FeatureTable.h +++ b/src/cpp/ripple/FeatureTable.h @@ -61,7 +61,7 @@ public: FeatureTable(uint32 majorityTime, int majorityFraction) : mMajorityTime(majorityTime), mMajorityFraction(majorityFraction), mFirstReport(0), mLastReport(0) - { addInitialFeatures(); } + { ; } void addInitialFeatures(); From 87bdcbbaed244c4c5e82f90fb6db87f847f5d445 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Thu, 30 May 2013 10:09:20 -0700 Subject: [PATCH 10/17] Updates --- src/cpp/ripple/FeatureTable.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/cpp/ripple/FeatureTable.h b/src/cpp/ripple/FeatureTable.h index aecc60b19b..b915232113 100644 --- a/src/cpp/ripple/FeatureTable.h +++ b/src/cpp/ripple/FeatureTable.h @@ -36,6 +36,10 @@ public: void setVeto() { mVetoed = true; } void setDefault() { mDefault = true; } + bool isDefault() { return mDefault; } + bool isSupported() { return mSupported; } + bool isVetoed() { return mVetoed; } + bool isEnabled() { return mEnabled; } }; From 5baf3a09bbd7647fe4b30dfabeebdc5154de9548 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Thu, 30 May 2013 12:19:08 -0700 Subject: [PATCH 11/17] Remove obsolete parameter --- src/cpp/ripple/FeatureTable.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/cpp/ripple/FeatureTable.h b/src/cpp/ripple/FeatureTable.h index b915232113..0adcf2bf3c 100644 --- a/src/cpp/ripple/FeatureTable.h +++ b/src/cpp/ripple/FeatureTable.h @@ -21,7 +21,6 @@ public: class FeatureState { public: - uint256 mHash; // Feature hash bool mVetoed; // We don't want this feature enabled bool mEnabled; bool mSupported; From a38e80eb6ba8d3c20b4d34bf8d07992888e5336c Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Thu, 30 May 2013 13:44:18 -0700 Subject: [PATCH 12/17] Function to get list of features supported by ledger. --- src/cpp/ripple/Ledger.cpp | 12 ++++++++++++ src/cpp/ripple/Ledger.h | 2 ++ 2 files changed, 14 insertions(+) diff --git a/src/cpp/ripple/Ledger.cpp b/src/cpp/ripple/Ledger.cpp index 227f350eb0..da9b4e105e 100644 --- a/src/cpp/ripple/Ledger.cpp +++ b/src/cpp/ripple/Ledger.cpp @@ -1327,6 +1327,18 @@ std::vector< std::pair > Ledger::getLedgerHashes() return ret; } +boost::unordered_set Ledger::getLedgerFeatures() +{ + boost::unordered_set usFeatures; + SLE::pointer sleFeatures = getSLEi(getLedgerFeatureIndex()); + if (sleFeatures) + { + BOOST_FOREACH(const uint256& uFeature, sleFeatures->getFieldV256(sfFeatures).peekValue()) + usFeatures.insert(uFeature); + } + return usFeatures; +} + // XRP to XRP not allowed. // Currencies must have appropriate issuer. // Currencies or accounts must differ. diff --git a/src/cpp/ripple/Ledger.h b/src/cpp/ripple/Ledger.h index b7e3cf524f..3ad00ca8cb 100644 --- a/src/cpp/ripple/Ledger.h +++ b/src/cpp/ripple/Ledger.h @@ -5,6 +5,7 @@ #include #include +#include #include #include @@ -219,6 +220,7 @@ public: static uint256 getLedgerFeatureIndex(); static uint256 getLedgerFeeIndex(); + boost::unordered_set getLedgerFeatures(); std::vector getNeededTransactionHashes(int max, SHAMapSyncFilter* filter); std::vector getNeededAccountStateHashes(int max, SHAMapSyncFilter* filter); From 69a904133fa4dee67fe7ffc62b8e5597fa0b8d27 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Thu, 30 May 2013 14:30:06 -0700 Subject: [PATCH 13/17] Block operation if a feature we don't support is enabled in the ledger. --- src/cpp/ripple/Application.cpp | 6 ++++++ src/cpp/ripple/ChangeTransactor.cpp | 4 ++++ src/cpp/ripple/FeatureTable.cpp | 7 +++++++ src/cpp/ripple/FeatureTable.h | 1 + src/cpp/ripple/Ledger.cpp | 9 +++------ src/cpp/ripple/Ledger.h | 2 +- src/cpp/ripple/NetworkOPs.cpp | 12 ++++++++++++ src/cpp/ripple/NetworkOPs.h | 3 +++ 8 files changed, 37 insertions(+), 7 deletions(-) diff --git a/src/cpp/ripple/Application.cpp b/src/cpp/ripple/Application.cpp index 4746d1ef86..6464e40102 100644 --- a/src/cpp/ripple/Application.cpp +++ b/src/cpp/ripple/Application.cpp @@ -547,6 +547,12 @@ bool serverOkay(std::string& reason) return false; } + if (theApp->getOPs().isFeatureBlocked()) + { + reason = "Server version too old"; + return false; + } + return true; } diff --git a/src/cpp/ripple/ChangeTransactor.cpp b/src/cpp/ripple/ChangeTransactor.cpp index fa9723732f..790b823686 100644 --- a/src/cpp/ripple/ChangeTransactor.cpp +++ b/src/cpp/ripple/ChangeTransactor.cpp @@ -85,6 +85,10 @@ TER ChangeTransactor::applyFeature() featureObject->setFieldV256(sfFeatures, features); mEngine->entryModify(featureObject); + theApp->getFeatureTable().enableFeature(feature); + if (!theApp->getFeatureTable().isFeatureSupported(feature)) + theApp->getOPs().setFeatureBlocked(); + return tesSUCCESS; } diff --git a/src/cpp/ripple/FeatureTable.cpp b/src/cpp/ripple/FeatureTable.cpp index e93a5821b4..1581d0d6ca 100644 --- a/src/cpp/ripple/FeatureTable.cpp +++ b/src/cpp/ripple/FeatureTable.cpp @@ -87,6 +87,13 @@ bool FeatureTable::isFeatureEnabled(const uint256& feature) return s && s->mEnabled; } +bool FeatureTable::isFeatureSupported(const uint256& feature) +{ + boost::mutex::scoped_lock sl(mMutex); + FeatureState *s = getCreateFeature(feature, false); + return s && s->mSupported; +} + FeatureTable::featureList_t FeatureTable::getVetoedFeatures() { featureList_t ret; diff --git a/src/cpp/ripple/FeatureTable.h b/src/cpp/ripple/FeatureTable.h index 0adcf2bf3c..222603d64e 100644 --- a/src/cpp/ripple/FeatureTable.h +++ b/src/cpp/ripple/FeatureTable.h @@ -75,6 +75,7 @@ public: bool disableFeature(const uint256& feature); bool isFeatureEnabled(const uint256& feature); + bool isFeatureSupported(const uint256& feature); void setEnabledFeatures(const std::vector& features); void setSupportedFeatures(const std::vector& features); diff --git a/src/cpp/ripple/Ledger.cpp b/src/cpp/ripple/Ledger.cpp index da9b4e105e..4e58876c54 100644 --- a/src/cpp/ripple/Ledger.cpp +++ b/src/cpp/ripple/Ledger.cpp @@ -1327,15 +1327,12 @@ std::vector< std::pair > Ledger::getLedgerHashes() return ret; } -boost::unordered_set Ledger::getLedgerFeatures() +std::vector Ledger::getLedgerFeatures() { - boost::unordered_set usFeatures; + std::vector usFeatures; SLE::pointer sleFeatures = getSLEi(getLedgerFeatureIndex()); if (sleFeatures) - { - BOOST_FOREACH(const uint256& uFeature, sleFeatures->getFieldV256(sfFeatures).peekValue()) - usFeatures.insert(uFeature); - } + usFeatures = sleFeatures->getFieldV256(sfFeatures).peekValue(); return usFeatures; } diff --git a/src/cpp/ripple/Ledger.h b/src/cpp/ripple/Ledger.h index 3ad00ca8cb..db56195efb 100644 --- a/src/cpp/ripple/Ledger.h +++ b/src/cpp/ripple/Ledger.h @@ -220,7 +220,7 @@ public: static uint256 getLedgerFeatureIndex(); static uint256 getLedgerFeeIndex(); - boost::unordered_set getLedgerFeatures(); + std::vector getLedgerFeatures(); std::vector getNeededTransactionHashes(int max, SHAMapSyncFilter* filter); std::vector getNeededAccountStateHashes(int max, SHAMapSyncFilter* filter); diff --git a/src/cpp/ripple/NetworkOPs.cpp b/src/cpp/ripple/NetworkOPs.cpp index 5eac4efe35..153647f4f7 100644 --- a/src/cpp/ripple/NetworkOPs.cpp +++ b/src/cpp/ripple/NetworkOPs.cpp @@ -31,6 +31,7 @@ void InfoSub::onSendEmpty() NetworkOPs::NetworkOPs(boost::asio::io_service& io_service, LedgerMaster* pLedgerMaster) : mMode(omDISCONNECTED), mNeedNetworkLedger(false), mProposing(false), mValidating(false), + mFeatureBlocked(false), mNetTimer(io_service), mLedgerMaster(pLedgerMaster), mCloseTimeOffset(0), mLastCloseProposers(0), mLastCloseConvergeTime(1000 * LEDGER_IDLE_INTERVAL), mLastCloseTime(0), mLastValidationTime(0), mFetchPack("FetchPack", 2048, 20), mLastFetchPack(0), mFetchSeq(static_cast(-1)), @@ -553,6 +554,12 @@ Json::Value NetworkOPs::getOwnerInfo(Ledger::pointer lpLedger, const RippleAddre // Other // +void NetworkOPs::setFeatureBlocked() +{ + mFeatureBlocked = true; + setMode(omTRACKING); +} + void NetworkOPs::setStateTimer() { mNetTimer.expires_from_now(boost::posix_time::milliseconds(LEDGER_GRANULARITY)); @@ -1047,6 +1054,9 @@ void NetworkOPs::setMode(OperatingMode om) om = omCONNECTED; } + if ((om > omTRACKING) && mFeatureBlocked) + om = omTRACKING; + if (mMode == om) return; @@ -1265,6 +1275,8 @@ Json::Value NetworkOPs::getServerInfo(bool human, bool admin) info["complete_ledgers"] = theApp->getLedgerMaster().getCompleteLedgers(); + if (mFeatureBlocked) + info["feature_blocked"] = true; size_t fp = mFetchPack.getCacheSize(); if (fp != 0) diff --git a/src/cpp/ripple/NetworkOPs.h b/src/cpp/ripple/NetworkOPs.h index ec1927170f..592573969d 100644 --- a/src/cpp/ripple/NetworkOPs.h +++ b/src/cpp/ripple/NetworkOPs.h @@ -115,6 +115,7 @@ protected: OperatingMode mMode; bool mNeedNetworkLedger; bool mProposing, mValidating; + bool mFeatureBlocked; boost::posix_time::ptime mConnectTime; boost::asio::deadline_timer mNetTimer; boost::shared_ptr mConsensus; @@ -309,6 +310,8 @@ public: void setProposing(bool p, bool v) { mProposing = p; mValidating = v; } bool isProposing() { return mProposing; } bool isValidating() { return mValidating; } + bool isFeatureBlocked() { return mFeatureBlocked; } + void setFeatureBlocked(); void consensusViewChange(); int getPreviousProposers() { return mLastCloseProposers; } int getPreviousConvergeTime() { return mLastCloseConvergeTime; } From 8acb34db91455c64bd569ca9a37a2d3f60de2663 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Thu, 30 May 2013 14:50:14 -0700 Subject: [PATCH 14/17] Feature friendly names. --- src/cpp/ripple/FeatureTable.cpp | 4 ++++ src/cpp/ripple/FeatureTable.h | 28 ++++++++++++++++------------ 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/src/cpp/ripple/FeatureTable.cpp b/src/cpp/ripple/FeatureTable.cpp index 1581d0d6ca..0fa2e2755a 100644 --- a/src/cpp/ripple/FeatureTable.cpp +++ b/src/cpp/ripple/FeatureTable.cpp @@ -9,6 +9,7 @@ void FeatureTable::addInitialFeatures() // getCreateFeature. Set any vetoes or defaults. A pointer to the FeatureState can be stashed testFeature = getCreateFeature(uint256("1234"), true); + testFeature->setFriendlyName("testFeature"); } FeatureState* FeatureTable::getCreateFeature(const uint256& featureHash, bool create) @@ -299,6 +300,9 @@ Json::Value FeatureTable::getJson(int) { Json::Value& v(ret[it.first.GetHex()] = Json::objectValue); + if (!it.second.mFriendlyName.empty()) + v["name"] = it.second.mFriendlyName; + v["supported"] = it.second.mSupported; if (it.second.mEnabled) diff --git a/src/cpp/ripple/FeatureTable.h b/src/cpp/ripple/FeatureTable.h index 222603d64e..0202386784 100644 --- a/src/cpp/ripple/FeatureTable.h +++ b/src/cpp/ripple/FeatureTable.h @@ -21,24 +21,28 @@ public: class FeatureState { public: - bool mVetoed; // We don't want this feature enabled - bool mEnabled; - bool mSupported; - bool mDefault; // Include in genesis ledger + bool mVetoed; // We don't want this feature enabled + bool mEnabled; + bool mSupported; + bool mDefault; // Include in genesis ledger - uint32 mFirstMajority; // First time we saw a majority (close time) - uint32 mLastMajority; // Most recent time we saw a majority (close time) + uint32 mFirstMajority; // First time we saw a majority (close time) + uint32 mLastMajority; // Most recent time we saw a majority (close time) + + std::string mFriendlyName; FeatureState() : mVetoed(false), mEnabled(false), mSupported(false), mDefault(false), mFirstMajority(0), mLastMajority(0) { ; } - void setVeto() { mVetoed = true; } - void setDefault() { mDefault = true; } - bool isDefault() { return mDefault; } - bool isSupported() { return mSupported; } - bool isVetoed() { return mVetoed; } - bool isEnabled() { return mEnabled; } + void setVeto() { mVetoed = true; } + void setDefault() { mDefault = true; } + bool isDefault() { return mDefault; } + bool isSupported() { return mSupported; } + bool isVetoed() { return mVetoed; } + bool isEnabled() { return mEnabled; } + const std::string& getFiendlyName() { return mFriendlyName; } + void setFriendlyName(const std::string& n) { mFriendlyName = n; } }; From 1d95d8f36c55034070a2e0dd4d434543fda14b80 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Thu, 30 May 2013 14:57:27 -0700 Subject: [PATCH 15/17] Clean up addition of initial features. --- src/cpp/ripple/FeatureTable.cpp | 24 ++++++++++++++++++++++-- src/cpp/ripple/FeatureTable.h | 2 ++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/cpp/ripple/FeatureTable.cpp b/src/cpp/ripple/FeatureTable.cpp index 0fa2e2755a..686a34df60 100644 --- a/src/cpp/ripple/FeatureTable.cpp +++ b/src/cpp/ripple/FeatureTable.cpp @@ -8,8 +8,7 @@ void FeatureTable::addInitialFeatures() // For each feature this version supports, construct the FeatureState object by calling // getCreateFeature. Set any vetoes or defaults. A pointer to the FeatureState can be stashed - testFeature = getCreateFeature(uint256("1234"), true); - testFeature->setFriendlyName("testFeature"); + testFeature = addKnownFeature("1234", "testFeature", false); } FeatureState* FeatureTable::getCreateFeature(const uint256& featureHash, bool create) @@ -41,6 +40,26 @@ FeatureState* FeatureTable::getCreateFeature(const uint256& featureHash, bool cr return &(it->second); } +FeatureState* FeatureTable::addKnownFeature(const char *featureID, const char *friendlyName, bool veto) +{ + uint256 hash; + hash.SetHex(featureID); + if (hash.isZero()) + { + assert(false); + return NULL; + } + FeatureState* f = getCreateFeature(hash, true); + + if (friendlyName != NULL) + f->setFriendlyName(friendlyName); + + f->mVetoed = veto; + f->mSupported = true; + + return f; +} + bool FeatureTable::vetoFeature(const uint256& feature) { boost::mutex::scoped_lock sl(mMutex); @@ -304,6 +323,7 @@ Json::Value FeatureTable::getJson(int) v["name"] = it.second.mFriendlyName; v["supported"] = it.second.mSupported; + v["vetoed"] = it.second.mVetoed; if (it.second.mEnabled) v["enabled"] = true; diff --git a/src/cpp/ripple/FeatureTable.h b/src/cpp/ripple/FeatureTable.h index 0202386784..623100b7e4 100644 --- a/src/cpp/ripple/FeatureTable.h +++ b/src/cpp/ripple/FeatureTable.h @@ -72,6 +72,8 @@ public: void addInitialFeatures(); + FeatureState* addKnownFeature(const char *featureID, const char *friendlyName, bool veto); + bool vetoFeature(const uint256& feature); bool unVetoFeature(const uint256& feature); From 349deba21843a363c9b7c1c74f105ca3b205d9df Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Thu, 30 May 2013 15:26:42 -0700 Subject: [PATCH 16/17] "Features" was supposed to be a Vector256. --- modules/ripple_data/types/ripple_SerializeDeclarations.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ripple_data/types/ripple_SerializeDeclarations.h b/modules/ripple_data/types/ripple_SerializeDeclarations.h index a36debe660..45593cce70 100644 --- a/modules/ripple_data/types/ripple_SerializeDeclarations.h +++ b/modules/ripple_data/types/ripple_SerializeDeclarations.h @@ -143,6 +143,7 @@ // vector of 256-bit FIELD(Indexes, VECTOR256, 1) FIELD(Hashes, VECTOR256, 2) + FIELD(Features, VECTOR256, 3) // inner object // OBJECT/1 is reserved for end of object @@ -164,6 +165,5 @@ FIELD(Necessary, ARRAY, 6) FIELD(Sufficient, ARRAY, 7) FIELD(AffectedNodes, ARRAY, 8) - FIELD(Features, ARRAY, 9) // vim:ts=4 From 6e220cc97e1d5f2c252652cfe18bcd98b17aa60d Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Fri, 31 May 2013 16:22:38 -0700 Subject: [PATCH 17/17] More feature RPC support. --- src/cpp/ripple/FeatureTable.cpp | 87 +++++++++++++++++++++------------ src/cpp/ripple/FeatureTable.h | 3 ++ src/cpp/ripple/RPCErr.cpp | 1 + src/cpp/ripple/RPCErr.h | 1 + src/cpp/ripple/RPCHandler.cpp | 10 +++- 5 files changed, 68 insertions(+), 34 deletions(-) diff --git a/src/cpp/ripple/FeatureTable.cpp b/src/cpp/ripple/FeatureTable.cpp index 686a34df60..8570175b30 100644 --- a/src/cpp/ripple/FeatureTable.cpp +++ b/src/cpp/ripple/FeatureTable.cpp @@ -40,6 +40,19 @@ FeatureState* FeatureTable::getCreateFeature(const uint256& featureHash, bool cr return &(it->second); } +uint256 FeatureTable::getFeature(const std::string& name) +{ + if (!name.empty()) + { + BOOST_FOREACH(featureMap_t::value_type& it, mFeatureMap) + { + if (name == it.second.mFriendlyName) + return it.first; + } + } + return uint256(); +} + FeatureState* FeatureTable::addKnownFeature(const char *featureID, const char *friendlyName, bool veto) { uint256 hash; @@ -317,50 +330,60 @@ Json::Value FeatureTable::getJson(int) boost::mutex::scoped_lock sl(mMutex); BOOST_FOREACH(const featureIt_t& it, mFeatureMap) { - Json::Value& v(ret[it.first.GetHex()] = Json::objectValue); + setJson(ret[it.first.GetHex()] = Json::objectValue, it.second); + } + } + return ret; +} - if (!it.second.mFriendlyName.empty()) - v["name"] = it.second.mFriendlyName; +void FeatureTable::setJson(Json::Value& v, const FeatureState& fs) +{ + if (!fs.mFriendlyName.empty()) + v["name"] = fs.mFriendlyName; - v["supported"] = it.second.mSupported; - v["vetoed"] = it.second.mVetoed; + v["supported"] = fs.mSupported; + v["vetoed"] = fs.mVetoed; - if (it.second.mEnabled) - v["enabled"] = true; + if (fs.mEnabled) + v["enabled"] = true; + else + { + v["enabled"] = false; + if (mLastReport != 0) + { + if (fs.mLastMajority == 0) + { + v["majority"] = false; + } else { - v["enabled"] = false; - if (mLastReport != 0) + if (fs.mFirstMajority != 0) { - if (it.second.mLastMajority == 0) - { - v["majority"] = false; - } + if (fs.mFirstMajority == mFirstReport) + v["majority_start"] = "start"; else - { - if (it.second.mFirstMajority != 0) - { - if (it.second.mFirstMajority == mFirstReport) - v["majority_start"] = "start"; - else - v["majority_start"] = it.second.mFirstMajority; - } - if (it.second.mLastMajority != 0) - { - if (it.second.mLastMajority == mLastReport) - v["majority_until"] = "now"; - else - v["majority_until"] = it.second.mLastMajority; - } - } + v["majority_start"] = fs.mFirstMajority; + } + if (fs.mLastMajority != 0) + { + if (fs.mLastMajority == mLastReport) + v["majority_until"] = "now"; + else + v["majority_until"] = fs.mLastMajority; } } - - if (it.second.mVetoed) - v["veto"] = true; } } + if (fs.mVetoed) + v["veto"] = true; +} + +Json::Value FeatureTable::getJson(const uint256& feature) +{ + Json::Value ret = Json::objectValue; + boost::mutex::scoped_lock sl(mMutex); + setJson(ret[feature.GetHex()] = Json::objectValue, *getCreateFeature(feature, true)); return ret; } diff --git a/src/cpp/ripple/FeatureTable.h b/src/cpp/ripple/FeatureTable.h index 623100b7e4..8f6ef3cca7 100644 --- a/src/cpp/ripple/FeatureTable.h +++ b/src/cpp/ripple/FeatureTable.h @@ -63,6 +63,7 @@ protected: FeatureState* getCreateFeature(const uint256& feature, bool create); bool shouldEnable (uint32 closeTime, const FeatureState& fs); + void setJson(Json::Value& v, const FeatureState&); public: @@ -73,6 +74,7 @@ public: void addInitialFeatures(); FeatureState* addKnownFeature(const char *featureID, const char *friendlyName, bool veto); + uint256 getFeature(const std::string& name); bool vetoFeature(const uint256& feature); bool unVetoFeature(const uint256& feature); @@ -94,6 +96,7 @@ public: void reportValidations(const FeatureSet&); Json::Value getJson(int); + Json::Value getJson(const uint256&); void doValidation(Ledger::ref lastClosedLedger, STObject& baseValidation); void doVoting(Ledger::ref lastClosedLedger, SHAMap::ref initialPosition); diff --git a/src/cpp/ripple/RPCErr.cpp b/src/cpp/ripple/RPCErr.cpp index b445279e5c..a7240ab9fd 100644 --- a/src/cpp/ripple/RPCErr.cpp +++ b/src/cpp/ripple/RPCErr.cpp @@ -18,6 +18,7 @@ Json::Value rpcError(int iError, Json::Value jvResult) { rpcACT_MALFORMED, "actMalformed", "Account malformed." }, { rpcACT_NOT_FOUND, "actNotFound", "Account not found." }, { rpcBAD_BLOB, "badBlob", "Blob must be a non-empty hex string." }, + { rpcBAD_FEATURE, "badFeature", "Feature unknown or invalid." }, { rpcBAD_ISSUER, "badIssuer", "Issuer account malformed." }, { rpcBAD_MARKET, "badMarket", "No such market." }, { rpcBAD_SECRET, "badSecret", "Secret does not match account." }, diff --git a/src/cpp/ripple/RPCErr.h b/src/cpp/ripple/RPCErr.h index 060e010ca0..e58d6d08c9 100644 --- a/src/cpp/ripple/RPCErr.h +++ b/src/cpp/ripple/RPCErr.h @@ -47,6 +47,7 @@ enum { rpcACT_MALFORMED, rpcQUALITY_MALFORMED, rpcBAD_BLOB, + rpcBAD_FEATURE, rpcBAD_ISSUER, rpcBAD_MARKET, rpcBAD_SECRET, diff --git a/src/cpp/ripple/RPCHandler.cpp b/src/cpp/ripple/RPCHandler.cpp index 974ef89e01..82b778cb19 100644 --- a/src/cpp/ripple/RPCHandler.cpp +++ b/src/cpp/ripple/RPCHandler.cpp @@ -2263,11 +2263,17 @@ Json::Value RPCHandler::doFeature(Json::Value jvRequest, int& cost, ScopedLock& return jvReply; } - if (!jvRequest.isMember("vote")) + uint256 uFeature = theApp->getFeatureTable().getFeature(jvRequest["feature"].asString()); + if (uFeature.isZero()) { - // WRITEME + uFeature.SetHex(jvRequest["feature"].asString()); + if (uFeature.isZero()) + return rpcError(rpcBAD_FEATURE); } + if (!jvRequest.isMember("vote")) + return theApp->getFeatureTable().getJson(uFeature); + // WRITEME return rpcError(rpcNOT_SUPPORTED); }