SETUP_LOG (FeatureTable) void FeatureTable::addInitialFeatures() { // For each feature this version supports, call enableFeature. // Permanent vetos can also be added here. } FeatureTable::FeatureState* FeatureTable::getCreateFeature(const uint256& featureHash, bool create) { // call with the mutex held featureMap_t::iterator it = mFeatureMap.find(featureHash); if (it == mFeatureMap.end()) { if (!create) return NULL; FeatureState *feature = &(mFeatureMap[featureHash]); { std::string query = "SELECT FirstMajority,LastMajority FROM Features WHERE hash='"; query.append(featureHash.GetHex()); query.append("';"); ScopedLock sl(theApp->getWalletDB()->getDBLock()); Database* db = theApp->getWalletDB()->getDB(); if (db->executeSQL(query) && db->startIterRows()) { feature->mFirstMajority = db->getBigInt("FirstMajority"); feature->mLastMajority = db->getBigInt("LastMajority"); db->endIterRows(); } } return feature; } return &(it->second); } bool FeatureTable::vetoFeature(const uint256& feature) { boost::mutex::scoped_lock sl(mMutex); FeatureState *s = getCreateFeature(feature, true); if (s->mVetoed) return false; s->mVetoed = true; return true; } bool FeatureTable::unVetoFeature(const uint256& feature) { boost::mutex::scoped_lock sl(mMutex); FeatureState *s = getCreateFeature(feature, false); if (!s || !s->mVetoed) return false; s->mVetoed = false; return true; } bool FeatureTable::enableFeature(const uint256& feature) { boost::mutex::scoped_lock sl(mMutex); FeatureState *s = getCreateFeature(feature, true); if (s->mEnabled) return false; s->mEnabled = true; return true; } bool FeatureTable::disableFeature(const uint256& feature) { boost::mutex::scoped_lock sl(mMutex); FeatureState *s = getCreateFeature(feature, false); if (!s || !s->mEnabled) return false; s->mEnabled = false; return true; } bool FeatureTable::isFeatureEnabled(const uint256& feature) { boost::mutex::scoped_lock sl(mMutex); FeatureState *s = getCreateFeature(feature, false); return s && s->mEnabled; } FeatureTable::featureList_t FeatureTable::getVetoedFeatures() { featureList_t ret; boost::mutex::scoped_lock sl(mMutex); BOOST_FOREACH(const featureIt_t& it, mFeatureMap) { if (it.second.mVetoed) ret.insert(it.first); } return ret; } FeatureTable::featureList_t FeatureTable::getEnabledFeatures() { featureList_t ret; boost::mutex::scoped_lock sl(mMutex); BOOST_FOREACH(const featureIt_t& it, mFeatureMap) { if (it.second.mEnabled) ret.insert(it.first); } return ret; } bool FeatureTable::shouldEnable(uint32 closeTime, const FeatureState& fs) { if (fs.mVetoed || fs.mEnabled || !fs.mSupported || (fs.mLastMajority != mLastReport)) return false; if (fs.mFirstMajority == mFirstReport) { // had a majority when we first started the server, relaxed check // WRITEME } // didn't have a majority when we first started the server, normal check return (fs.mLastMajority - fs.mFirstMajority) > mMajorityTime; } FeatureTable::featureList_t FeatureTable::getFeaturesToEnable(uint32 closeTime) { featureList_t ret; boost::mutex::scoped_lock sl(mMutex); if (mLastReport != 0) { BOOST_FOREACH(const featureIt_t& it, mFeatureMap) { if (shouldEnable(closeTime, it.second)) ret.insert(it.first); } } return ret; } FeatureTable::featureList_t FeatureTable::getDesiredFeatures() { featureList_t ret; boost::mutex::scoped_lock sl(mMutex); BOOST_FOREACH(const featureIt_t& it, mFeatureMap) { if (it.second.mSupported && !it.second.mEnabled && !it.second.mVetoed) ret.insert(it.first); } return ret; } void FeatureTable::reportValidations(const FeatureSet& set) { if (set.mTrustedValidations == 0) return; int threshold = (set.mTrustedValidations * mMajorityFraction) / 256; typedef std::map::value_type u256_int_pair; boost::mutex::scoped_lock sl(mMutex); if (mFirstReport == 0) mFirstReport = set.mCloseTime; std::vector changedFeatures; changedFeatures.resize(set.mVotes.size()); BOOST_FOREACH(const u256_int_pair& it, set.mVotes) { FeatureState& state = mFeatureMap[it.first]; WriteLog (lsDEBUG, FeatureTable) << "Feature " << it.first.GetHex() << " has " << it.second << " votes, needs " << threshold; if (it.second >= threshold) { // we have a majority state.mLastMajority = set.mCloseTime; if (state.mFirstMajority == 0) { WriteLog (lsWARNING, FeatureTable) << "Feature " << it.first << " attains a majority vote"; state.mFirstMajority = set.mCloseTime; changedFeatures.push_back(it.first); } } else // we have no majority { if (state.mFirstMajority != 0) { WriteLog (lsWARNING, FeatureTable) << "Feature " << it.first << " loses majority vote"; state.mFirstMajority = 0; state.mLastMajority = 0; changedFeatures.push_back(it.first); } } } mLastReport = set.mCloseTime; if (!changedFeatures.empty()) { // WRITEME write changed features to SQL db } } void FeatureTable::setEnabledFeatures(const std::vector& features) { boost::mutex::scoped_lock sl(mMutex); BOOST_FOREACH(featureIt_t& it, mFeatureMap) { it.second.mEnabled = false; } BOOST_FOREACH(const uint256& it, features) { mFeatureMap[it].mEnabled = true; } } void FeatureTable::setSupportedFeatures(const std::vector& features) { boost::mutex::scoped_lock sl(mMutex); BOOST_FOREACH(featureIt_t& it, mFeatureMap) { it.second.mSupported = false; } BOOST_FOREACH(const uint256& it, features) { mFeatureMap[it].mSupported = true; } } void FeatureTable::doValidation(Ledger::ref lastClosedLedger, STObject& baseValidation) { featureList_t lFeatures = getDesiredFeatures(); if (lFeatures.empty()) return; STVector256 vFeatures(sfFeatures); BOOST_FOREACH(const uint256& uFeature, lFeatures) { vFeatures.addValue(uFeature); } vFeatures.sort(); baseValidation.setFieldV256(sfFeatures, vFeatures); } void FeatureTable::doVoting(Ledger::ref lastClosedLedger, SHAMap::ref initialPosition) { featureList_t lFeatures = getFeaturesToEnable(lastClosedLedger->getCloseTimeNC()); if (lFeatures.empty()) return; BOOST_FOREACH(const uint256& uFeature, lFeatures) { WriteLog (lsWARNING, FeatureTable) << "We are voting for feature " << uFeature; SerializedTransaction trans(ttFEATURE); trans.setFieldAccount(sfAccount, uint160()); trans.setFieldH256(sfFeature, uFeature); uint256 txID = trans.getTransactionID(); WriteLog (lsWARNING, FeatureTable) << "Vote: " << txID; Serializer s; trans.add(s, true); SHAMapItem::pointer tItem = boost::make_shared(txID, s.peekData()); if (!initialPosition->addGiveItem(tItem, true, false)) { WriteLog (lsWARNING, FeatureTable) << "Ledger already had feature transaction"; } } } Json::Value FeatureTable::getJson(int) { Json::Value ret(Json::objectValue); { boost::mutex::scoped_lock sl(mMutex); BOOST_FOREACH(const featureIt_t& it, mFeatureMap) { Json::Value v(Json::objectValue); v["supported"] = it.second.mSupported; if (it.second.mEnabled) v["enabled"] = true; else { v["enabled"] = false; if (mLastReport != 0) { if (it.second.mLastMajority == 0) v["majority"] = false; 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; } } } } if (it.second.mVetoed) v["veto"] = true; ret[it.first.GetHex()] = v; } } return ret; } template class VotableInteger { protected: INT mCurrent; // The current setting INT mTarget; // The setting we want std::map mVoteMap; public: VotableInteger(INT current, INT target) : mCurrent(current), mTarget(target) { ++mVoteMap[mTarget]; // Add our vote } bool mayVote() { return mCurrent != mTarget; // If we love the current setting, we will not vote } void addVote(INT vote) { ++mVoteMap[vote]; } void noVote() { addVote(mCurrent); } INT getVotes() { INT ourVote = mCurrent; int weight = 0; typedef typename std::map::value_type mapVType; BOOST_FOREACH(const mapVType& value, mVoteMap) { // Take most voted value between current and target, inclusive if ((value.first <= std::max(mTarget, mCurrent)) && (value.first >= std::min(mTarget, mCurrent)) && (value.second > weight)) { ourVote = value.first; weight = value.second; } } return ourVote; } }; // vim:ts=4