Merge branch 'feature' into develop

This commit is contained in:
JoelKatz
2013-06-05 16:40:28 -07:00
14 changed files with 228 additions and 49 deletions

View File

@@ -206,6 +206,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";
@@ -437,6 +439,8 @@ void Application::startNewLedger()
{
Ledger::pointer firstLedger = boost::make_shared<Ledger>(rootAddress, SYSTEM_CURRENCY_START);
assert(!!firstLedger->getAccountState(rootAddress));
// WRITEME: Add any default features
// WRITEME: Set default fee/reserve
firstLedger->updateHash();
firstLedger->setClosed();
firstLedger->setAccepted();
@@ -548,6 +552,12 @@ bool serverOkay(std::string& reason)
return false;
}
if (theApp->getOPs().isFeatureBlocked())
{
reason = "Server version too old";
return false;
}
return true;
}

View File

@@ -310,6 +310,20 @@ Json::Value RPCParser::parseEvented(const Json::Value& jvParams)
return rpcError(rpcNO_EVENTS);
}
// feature [<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<bool>(jvParams[1u].asString());
return jvRequest;
}
// get_counts [<min_count>]
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 },

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -1,13 +1,17 @@
SETUP_LOG (FeatureTable)
FeatureState* testFeature = NULL;
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
testFeature = addKnownFeature("1234", "testFeature", false);
}
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())
@@ -36,6 +40,39 @@ FeatureTable::FeatureState* FeatureTable::getCreateFeature(const uint256& featur
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;
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);
@@ -83,6 +120,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;
@@ -193,7 +237,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 +305,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,46 +330,60 @@ Json::Value FeatureTable::getJson(int)
boost::mutex::scoped_lock sl(mMutex);
BOOST_FOREACH(const featureIt_t& it, mFeatureMap)
{
Json::Value v(Json::objectValue);
setJson(ret[it.first.GetHex()] = Json::objectValue, it.second);
}
}
return ret;
}
v["supported"] = it.second.mSupported;
void FeatureTable::setJson(Json::Value& v, const FeatureState& fs)
{
if (!fs.mFriendlyName.empty())
v["name"] = fs.mFriendlyName;
if (it.second.mEnabled)
v["supported"] = fs.mSupported;
v["vetoed"] = fs.mVetoed;
if (fs.mEnabled)
v["enabled"] = true;
else
{
v["enabled"] = false;
if (mLastReport != 0)
{
if (it.second.mLastMajority == 0)
if (fs.mLastMajority == 0)
{
v["majority"] = false;
}
else
{
if (it.second.mFirstMajority != 0)
if (fs.mFirstMajority != 0)
{
if (it.second.mFirstMajority == mFirstReport)
if (fs.mFirstMajority == mFirstReport)
v["majority_start"] = "start";
else
v["majority_start"] = it.second.mFirstMajority;
v["majority_start"] = fs.mFirstMajority;
}
if (it.second.mLastMajority != 0)
if (fs.mLastMajority != 0)
{
if (it.second.mLastMajority == mLastReport)
if (fs.mLastMajority == mLastReport)
v["majority_until"] = "now";
else
v["majority_until"] = it.second.mLastMajority;
v["majority_until"] = fs.mLastMajority;
}
}
}
}
if (it.second.mVetoed)
if (fs.mVetoed)
v["veto"] = true;
}
ret[it.first.GetHex()] = v;
}
}
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;
}
@@ -350,6 +423,7 @@ public:
typedef typename std::map<INT, int>::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))

View File

@@ -18,22 +18,37 @@ public:
void addVote(const uint256& feature) { ++mVotes[feature]; }
};
class FeatureTable
class FeatureState
{
protected:
class FeatureState
{
public:
public:
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), mFirstMajority(0), mLastMajority(0) { ; }
};
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; }
const std::string& getFiendlyName() { return mFriendlyName; }
void setFriendlyName(const std::string& n) { mFriendlyName = n; }
};
class FeatureTable
{
protected:
typedef boost::unordered_map<uint256, FeatureState> featureMap_t;
typedef std::pair<const uint256, FeatureState> featureIt_t;
@@ -48,15 +63,19 @@ protected:
FeatureState* getCreateFeature(const uint256& feature, bool create);
bool shouldEnable (uint32 closeTime, const FeatureState& fs);
void setJson(Json::Value& v, const FeatureState&);
public:
FeatureTable(uint32 majorityTime, int majorityFraction)
: mMajorityTime(majorityTime), mMajorityFraction(majorityFraction), mFirstReport(0), mLastReport(0)
{ addInitialFeatures(); }
{ ; }
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);
@@ -64,6 +83,7 @@ public:
bool disableFeature(const uint256& feature);
bool isFeatureEnabled(const uint256& feature);
bool isFeatureSupported(const uint256& feature);
void setEnabledFeatures(const std::vector<uint256>& features);
void setSupportedFeatures(const std::vector<uint256>& features);
@@ -76,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);

View File

@@ -1327,6 +1327,15 @@ std::vector< std::pair<uint32, uint256> > Ledger::getLedgerHashes()
return ret;
}
std::vector<uint256> Ledger::getLedgerFeatures()
{
std::vector<uint256> usFeatures;
SLE::pointer sleFeatures = getSLEi(getLedgerFeatureIndex());
if (sleFeatures)
usFeatures = sleFeatures->getFieldV256(sfFeatures).peekValue();
return usFeatures;
}
// XRP to XRP not allowed.
// Currencies must have appropriate issuer.
// Currencies or accounts must differ.

View File

@@ -5,6 +5,7 @@
#include <list>
#include <boost/shared_ptr.hpp>
#include <boost/unordered_set.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
@@ -219,6 +220,7 @@ public:
static uint256 getLedgerFeatureIndex();
static uint256 getLedgerFeeIndex();
std::vector<uint256> getLedgerFeatures();
std::vector<uint256> getNeededTransactionHashes(int max, SHAMapSyncFilter* filter);
std::vector<uint256> getNeededAccountStateHashes(int max, SHAMapSyncFilter* filter);

View File

@@ -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<uint32>(-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)

View File

@@ -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<LedgerConsensus> 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; }

View File

@@ -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." },

View File

@@ -47,6 +47,7 @@ enum {
rpcACT_MALFORMED,
rpcQUALITY_MALFORMED,
rpcBAD_BLOB,
rpcBAD_FEATURE,
rpcBAD_ISSUER,
rpcBAD_MARKET,
rpcBAD_SECRET,

View File

@@ -2255,6 +2255,30 @@ 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;
}
uint256 uFeature = theApp->getFeatureTable().getFeature(jvRequest["feature"].asString());
if (uFeature.isZero())
{
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);
}
// {
// min_count: <number> // optional, defaults to 10
// }
@@ -3461,6 +3485,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 },

View File

@@ -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);