From 7545ad4d11acf7e9f29fe75c2582123e61c3bef3 Mon Sep 17 00:00:00 2001 From: csoler Date: Thu, 4 Aug 2016 11:43:35 +0200 Subject: [PATCH] simplied/improved reputation system. Now ids can be banned based on their owner node using a single checkbox in the Person tab, with immediate effect --- libretroshare/src/retroshare/rsreputations.h | 12 +- .../src/serialiser/rsgxsreputationitems.cc | 86 ++++- .../src/serialiser/rsgxsreputationitems.h | 21 ++ libretroshare/src/services/p3gxsreputation.cc | 349 ++++++++++++------ libretroshare/src/services/p3gxsreputation.h | 29 +- libretroshare/src/services/p3idservice.cc | 22 +- retroshare-gui/src/gui/Identity/IdDialog.cpp | 16 + retroshare-gui/src/gui/Identity/IdDialog.h | 1 + retroshare-gui/src/gui/Identity/IdDialog.ui | 15 +- .../src/gui/settings/PeoplePage.cpp | 8 - retroshare-gui/src/gui/settings/PeoplePage.ui | 44 --- 11 files changed, 407 insertions(+), 196 deletions(-) diff --git a/libretroshare/src/retroshare/rsreputations.h b/libretroshare/src/retroshare/rsreputations.h index fdb89b8b5..dac28ad38 100644 --- a/libretroshare/src/retroshare/rsreputations.h +++ b/libretroshare/src/retroshare/rsreputations.h @@ -50,12 +50,13 @@ public: }; virtual bool setOwnOpinion(const RsGxsId& key_id, const Opinion& op) =0; - virtual bool getReputationInfo(const RsGxsId& id,const RsPgpId& owner_id,ReputationInfo& info) =0 ; + virtual bool getReputationInfo(const RsGxsId& id, const RsPgpId &ownerNode, ReputationInfo& info) =0; // parameters - virtual void setNodeAutoBanThreshold(uint32_t n) =0; - virtual uint32_t nodeAutoBanThreshold() =0; + // virtual void setNodeAutoBanThreshold(uint32_t n) =0; + // virtual uint32_t nodeAutoBanThreshold() =0; + virtual void setNodeAutoPositiveOpinionForContacts(bool b) =0; virtual bool nodeAutoPositiveOpinionForContacts() =0; virtual float nodeAutoBanIdentitiesLimit() =0; @@ -64,7 +65,10 @@ public: // This one is a proxy designed to allow fast checking of a GXS id. // it basically returns true if assessment is not ASSESSMENT_OK - virtual bool isIdentityBanned(const RsGxsId& id,const RsPgpId& owner_node) =0; + virtual bool isIdentityBanned(const RsGxsId& id) =0; + + virtual bool isNodeBanned(const RsPgpId& id) =0; + virtual void banNode(const RsPgpId& id,bool b) =0; }; // To access reputations from anywhere diff --git a/libretroshare/src/serialiser/rsgxsreputationitems.cc b/libretroshare/src/serialiser/rsgxsreputationitems.cc index 35c07959b..7a7c9e7f8 100644 --- a/libretroshare/src/serialiser/rsgxsreputationitems.cc +++ b/libretroshare/src/serialiser/rsgxsreputationitems.cc @@ -74,6 +74,11 @@ void RsGxsReputationUpdateItem::clear() mOpinions.clear() ; } +void RsGxsReputationBannedNodeSetItem::clear() +{ + mKnownIdentities.TlvClear(); +} + /*************************************************************************/ /*************************************************************************/ /*************************************************************************/ @@ -128,6 +133,17 @@ std::ostream& RsGxsReputationRequestItem::print(std::ostream &out, uint16_t inde printRsItemEnd(out, "RsReputationRequestItem", indent); return out; } +std::ostream& RsGxsReputationBannedNodeSetItem::print(std::ostream &out, uint16_t indent) +{ + printRsItemBase(out, "RsReputationBannedNodeSetItem", indent); + + out << "last update: " << time(NULL) - mLastActivityTS << " secs ago." << std::endl; + out << "PGP id: " << mPgpId << std::endl; + out << "Known ids: " << mKnownIdentities.ids.size() << std::endl; + + printRsItemEnd(out, "RsReputationRequestItem", indent); + return out; +} /*************************************************************************/ uint32_t RsGxsReputationConfigItem::serial_size() const @@ -135,8 +151,8 @@ uint32_t RsGxsReputationConfigItem::serial_size() const uint32_t s = 8; /* header */ s += mPeerId.serial_size() ; // PeerId - s += 4 ; // mLatestUpdate - s += 4 ; // mLastQuery + s += 4 ; // mLatestUpdate + s += 4 ; // mLastQuery return s ; } @@ -158,6 +174,17 @@ uint32_t RsGxsReputationSetItem::serial_size() const return s ; } +uint32_t RsGxsReputationBannedNodeSetItem::serial_size() const +{ + uint32_t s = 8; /* header */ + + s += RsPgpId::serial_size() ; // mPgpId + s += 4 ; // mLastActivityTS; + s += mKnownIdentities.TlvSize(); // mKnownIdentities + + return s ; +} + uint32_t RsGxsReputationUpdateItem::serial_size() const { uint32_t s = 8; /* header */ @@ -236,6 +263,29 @@ bool RsGxsReputationSetItem::serialise(void *data, uint32_t& pktsize) const return ok; } + +bool RsGxsReputationBannedNodeSetItem::serialise(void *data, uint32_t& pktsize) const +{ + uint32_t tlvsize ; + uint32_t offset=0; + + if(!serialise_header(data,pktsize,tlvsize,offset)) + return false ; + + bool ok = true; + + ok &= mPgpId.serialise(data, tlvsize, offset) ; + ok &= setRawUInt32(data, tlvsize, &offset, mLastActivityTS); + ok &= mKnownIdentities.SetTlv(data, tlvsize, &offset) ; + + if (offset != tlvsize) + { + ok = false; + std::cerr << "RsGxsReputationSetItem::serialisedata() size error! " << std::endl; + } + + return ok; +} bool RsGxsReputationUpdateItem::serialise(void *data, uint32_t& pktsize) const { uint32_t tlvsize ; @@ -308,7 +358,28 @@ RsGxsReputationConfigItem *RsGxsReputationSerialiser::deserialiseReputationConfi return item; } +RsGxsReputationBannedNodeSetItem *RsGxsReputationSerialiser::deserialiseReputationBannedNodeSetItem(void *data,uint32_t size) +{ + uint32_t offset = 8; // skip the header + uint32_t rssize = getRsItemSize(data); + bool ok = true ; + RsGxsReputationBannedNodeSetItem *item = new RsGxsReputationBannedNodeSetItem() ; + + /* add mandatory parts first */ + ok &= item->mPgpId.deserialise(data, size, offset) ; + ok &= getRawUInt32(data, size, &offset, &item->mLastActivityTS); + ok &= item->mKnownIdentities.GetTlv(data,size,&offset) ; + + if (offset != rssize || !ok) + { + std::cerr << __PRETTY_FUNCTION__ << ": error while deserialising! Item will be dropped." << std::endl; + delete item; + return NULL ; + } + + return item; +} RsGxsReputationSetItem *RsGxsReputationSerialiser::deserialiseReputationSetItem_deprecated(void *data,uint32_t tlvsize) { uint32_t offset = 8; // skip the header @@ -462,11 +533,12 @@ RsItem *RsGxsReputationSerialiser::deserialise(void *data, uint32_t *pktsize) switch(getRsItemSubType(rstype)) { - case RS_PKT_SUBTYPE_GXS_REPUTATION_SET_ITEM : return deserialiseReputationSetItem (data, *pktsize); - case RS_PKT_SUBTYPE_GXS_REPUTATION_SET_ITEM_deprecated: return deserialiseReputationSetItem_deprecated(data, *pktsize); - case RS_PKT_SUBTYPE_GXS_REPUTATION_UPDATE_ITEM : return deserialiseReputationUpdateItem (data, *pktsize); - case RS_PKT_SUBTYPE_GXS_REPUTATION_REQUEST_ITEM : return deserialiseReputationRequestItem (data, *pktsize); - case RS_PKT_SUBTYPE_GXS_REPUTATION_CONFIG_ITEM : return deserialiseReputationConfigItem (data, *pktsize); + case RS_PKT_SUBTYPE_GXS_REPUTATION_SET_ITEM : return deserialiseReputationSetItem (data, *pktsize); + case RS_PKT_SUBTYPE_GXS_REPUTATION_SET_ITEM_deprecated : return deserialiseReputationSetItem_deprecated(data, *pktsize); + case RS_PKT_SUBTYPE_GXS_REPUTATION_BANNED_NODE_SET_ITEM: return deserialiseReputationBannedNodeSetItem(data, *pktsize); + case RS_PKT_SUBTYPE_GXS_REPUTATION_UPDATE_ITEM : return deserialiseReputationUpdateItem (data, *pktsize); + case RS_PKT_SUBTYPE_GXS_REPUTATION_REQUEST_ITEM : return deserialiseReputationRequestItem (data, *pktsize); + case RS_PKT_SUBTYPE_GXS_REPUTATION_CONFIG_ITEM : return deserialiseReputationConfigItem (data, *pktsize); default: std::cerr << "RsGxsReputationSerialiser::deserialise(): unknown item subtype " << std::hex<< rstype << std::dec << std::endl; diff --git a/libretroshare/src/serialiser/rsgxsreputationitems.h b/libretroshare/src/serialiser/rsgxsreputationitems.h index e5a062757..4bf55e0d8 100644 --- a/libretroshare/src/serialiser/rsgxsreputationitems.h +++ b/libretroshare/src/serialiser/rsgxsreputationitems.h @@ -30,6 +30,7 @@ #include "serialiser/rsserviceids.h" #include "serialiser/rsserial.h" +#include "serialiser/rstlvidset.h" #include "retroshare/rsgxsifacetypes.h" #define RS_PKT_SUBTYPE_GXS_REPUTATION_CONFIG_ITEM 0x01 @@ -38,6 +39,7 @@ #define RS_PKT_SUBTYPE_GXS_REPUTATION_REQUEST_ITEM 0x04 #define RS_PKT_SUBTYPE_GXS_REPUTATION_SET_ITEM_deprecated 0x05 #define RS_PKT_SUBTYPE_GXS_REPUTATION_SET_ITEM 0x06 +#define RS_PKT_SUBTYPE_GXS_REPUTATION_BANNED_NODE_SET_ITEM 0x07 /**************************************************************************/ class RsReputationItem: public RsItem @@ -118,6 +120,24 @@ public: RsPgpId mOwnerNodeId; std::map mOpinions; // RsPeerId -> Opinion. }; + +class RsGxsReputationBannedNodeSetItem: public RsReputationItem +{ +public: + RsGxsReputationBannedNodeSetItem() :RsReputationItem(RS_PKT_SUBTYPE_GXS_REPUTATION_BANNED_NODE_SET_ITEM) {} + + virtual ~RsGxsReputationBannedNodeSetItem() {} + virtual void clear(); + std::ostream &print(std::ostream &out, uint16_t indent = 0); + + virtual bool serialise(void *data,uint32_t& size) const ; + virtual uint32_t serial_size() const ; + + RsPgpId mPgpId ; + uint32_t mLastActivityTS ; + RsTlvGxsIdSet mKnownIdentities ; +}; + class RsGxsReputationUpdateItem: public RsReputationItem { public: @@ -173,6 +193,7 @@ private: static RsGxsReputationSetItem *deserialiseReputationSetItem_deprecated (void *data, uint32_t size); static RsGxsReputationUpdateItem *deserialiseReputationUpdateItem (void *data, uint32_t size); static RsGxsReputationRequestItem *deserialiseReputationRequestItem (void *data, uint32_t size); + static RsGxsReputationBannedNodeSetItem *deserialiseReputationBannedNodeSetItem (void *data, uint32_t size); }; /**************************************************************************/ diff --git a/libretroshare/src/services/p3gxsreputation.cc b/libretroshare/src/services/p3gxsreputation.cc index 88f2855b8..68933e4a8 100644 --- a/libretroshare/src/services/p3gxsreputation.cc +++ b/libretroshare/src/services/p3gxsreputation.cc @@ -139,6 +139,7 @@ static const uint32_t PGP_AUTO_BAN_THRESHOLD_DEFAULT = 2 ; // above t static const uint32_t IDENTITY_FLAGS_UPDATE_DELAY = 100 ; // static const uint32_t BANNED_NODES_UPDATE_DELAY = 313 ; // update approx every 5 mins. Chosen to not be a multiple of IDENTITY_FLAGS_UPDATE_DELAY static const uint32_t REPUTATION_INFO_KEEP_DELAY = 86400*35; // remove old reputation info 5 days after last usage limit, in case the ID would come back.. +static const uint32_t BANNED_NODES_INACTIVITY_KEEP = 86400*60; // remove all info about banned nodes after 2 months of inactivity p3GxsReputation::p3GxsReputation(p3LinkMgr *lm) :p3Service(), p3Config(), @@ -146,7 +147,7 @@ p3GxsReputation::p3GxsReputation(p3LinkMgr *lm) { addSerialType(new RsGxsReputationSerialiser()); - mPgpAutoBanThreshold = PGP_AUTO_BAN_THRESHOLD_DEFAULT ; + //mPgpAutoBanThreshold = PGP_AUTO_BAN_THRESHOLD_DEFAULT ; mRequestTime = 0; mStoreTime = 0; mReputationsUpdated = false; @@ -154,6 +155,7 @@ p3GxsReputation::p3GxsReputation(p3LinkMgr *lm) mLastIdentityFlagsUpdate = time(NULL) - 3; mAverageActiveFriends = 0 ; mLastBannedNodesUpdate = 0 ; + mBannedNodesProxyNeedsUpdate = false; mAutoBanIdentitiesLimit = REPUTATION_ASSESSMENT_THRESHOLD_X1; mAutoSetPositiveOptionToContacts = true; // default @@ -200,9 +202,15 @@ int p3GxsReputation::tick() if(now > BANNED_NODES_UPDATE_DELAY+mLastBannedNodesUpdate) // 613 is not a multiple of 100, to avoid piling up work { updateIdentityFlags() ; // needed before updateBannedNodesList! - updateBannedNodesList(); + updateBannedNodesProxy(); mLastBannedNodesUpdate = now ; } + + if(mBannedNodesProxyNeedsUpdate) + { + updateBannedNodesProxy(); + mBannedNodesProxyNeedsUpdate = false ; + } #ifdef DEBUG_REPUTATION static time_t last_debug_print = time(NULL) ; @@ -216,21 +224,21 @@ int p3GxsReputation::tick() return 0; } -void p3GxsReputation::setNodeAutoBanThreshold(uint32_t n) -{ - RsStackMutex stack(mReputationMtx); /****** LOCKED MUTEX *******/ - - if(n != mPgpAutoBanThreshold) - { - mLastBannedNodesUpdate = 0 ; - mPgpAutoBanThreshold = n ; - IndicateConfigChanged() ; - } -} -uint32_t p3GxsReputation::nodeAutoBanThreshold() -{ - return mPgpAutoBanThreshold ; -} +// void p3GxsReputation::setNodeAutoBanThreshold(uint32_t n) +// { +// RsStackMutex stack(mReputationMtx); /****** LOCKED MUTEX *******/ +// +// if(n != mPgpAutoBanThreshold) +// { +// mLastBannedNodesUpdate = 0 ; +// mPgpAutoBanThreshold = n ; +// IndicateConfigChanged() ; +// } +// } +// uint32_t p3GxsReputation::nodeAutoBanThreshold() +// { +// return mPgpAutoBanThreshold ; +// } void p3GxsReputation::setNodeAutoPositiveOpinionForContacts(bool b) { @@ -284,33 +292,21 @@ class ZeroInitCnt operator uint32_t() const { return cnt ; } }; -void p3GxsReputation::updateBannedNodesList() +void p3GxsReputation::updateBannedNodesProxy() { -#ifdef DEBUG_REPUTATION - std::cerr << "Updating PGP ban list based on signed GxsIds to ban" << std::endl; -#endif - std::map tmpreps ; +//#ifdef DEBUG_REPUTATION +// std::cerr << "Updating PGP ban list based on signed GxsIds to ban. Ban threshold = " << mPgpAutoBanThreshold << std::endl; +//#endif + // This function keeps the Banned GXS id proxy up to date. + // - RsStackMutex stack(mReputationMtx); /****** LOCKED MUTEX *******/ - tmpreps = mReputations ; + RsStackMutex stack(mReputationMtx); /****** LOCKED MUTEX *******/ - std::map pgp_ids_to_ban ; + mPerNodeBannedIdsProxy.clear(); - for( std::map::iterator rit = tmpreps.begin();rit!=tmpreps.end();++rit) - if((rit->second.mIdentityFlags & REPUTATION_IDENTITY_FLAG_PGP_LINKED) && !rit->second.mOwnerNode.isNull() && rit->second.mOwnOpinion == p3GxsReputation::OPINION_NEGATIVE) - ++pgp_ids_to_ban[rit->second.mOwnerNode] ; - - mBannedPgpIds.clear() ; - - if(mPgpAutoBanThreshold > 0) - for(std::map::const_iterator it(pgp_ids_to_ban.begin());it!=pgp_ids_to_ban.end();++it) - { -#ifdef DEBUG_REPUTATION - std::cerr << "PGP Id: " << it->first << ". Ban count=" << it->second << " - " << (( it->second >= mPgpAutoBanThreshold)?"Banned!":"OK" ) << std::endl; -#endif - if(it->second >= mPgpAutoBanThreshold) - mBannedPgpIds.insert(it->first) ; - } + for( std::map::iterator rit = mBannedPgpIds.begin();rit!=mBannedPgpIds.end();++rit) + for(std::set::const_iterator it(rit->second.known_identities.begin());it!=rit->second.known_identities.end();++it) + mPerNodeBannedIdsProxy.insert(*it) ; } void p3GxsReputation::updateIdentityFlags() @@ -326,7 +322,7 @@ void p3GxsReputation::updateIdentityFlags() #endif for( std::map::iterator rit = mReputations.begin();rit!=mReputations.end();++rit) - if(rit->second.mIdentityFlags & REPUTATION_IDENTITY_FLAG_NEEDS_UPDATE) + if( (rit->second.mIdentityFlags & REPUTATION_IDENTITY_FLAG_NEEDS_UPDATE) && (mPerNodeBannedIdsProxy.find(rit->first) == mPerNodeBannedIdsProxy.end())) to_update.push_back(rit->first) ; } @@ -387,9 +383,9 @@ void p3GxsReputation::cleanup() #endif std::cerr << "p3GxsReputation::cleanup() " << std::endl; - // remove optionions about identities that do not exist anymore. That will in particular avoid asking p3idservice about deleted - // identities, which would cause an excess of hits to the database. - // We do it in two steps to avoid a deadlock when calling rsIdentity from here. + // Remove opinions about identities that do not exist anymore. That will in particular avoid asking p3idservice about deleted + // identities, which would cause an excess of hits to the database. We do it in two steps to avoid a deadlock when calling rsIdentity from here. + // Also, neutral opinions for banned PGP linked nodes are kept, so as to be able to not request them again. bool updated = false ; time_t now = time(NULL) ; @@ -400,15 +396,15 @@ void p3GxsReputation::cleanup() RsStackMutex stack(mReputationMtx); /****** LOCKED MUTEX *******/ for(std::map::iterator it(mReputations.begin());it!=mReputations.end();) - if(it->second.mOpinions.empty() && it->second.mOwnOpinion == RsReputations::OPINION_NEUTRAL) + if(it->second.mOpinions.empty() && it->second.mOwnOpinion == RsReputations::OPINION_NEUTRAL && (it->second.mOwnerNode.isNull())) { - std::map::iterator tmp(it) ; +#ifdef DEBUG_REPUTATION + std::cerr << " ID " << it->first << ": own is neutral and no opinions from friends => remove entry" << std::endl; +#endif + std::map::iterator tmp(it) ; ++tmp ; mReputations.erase(it) ; it = tmp ; -#ifdef DEBUG_REPUTATION - std::cerr << " ID " << it->first << ": own is neutral and no opinions from friends => remove entry" << std::endl; -#endif updated = true ; } else @@ -424,12 +420,31 @@ void p3GxsReputation::cleanup() #ifdef DEBUG_REPUTATION std::cerr << " Identity " << *it << " has a last usage TS of " << now - rsIdentity->getLastUsageTS(*it) << " secs ago: deleting it." << std::endl; #endif - RsStackMutex stack(mReputationMtx); /****** LOCKED MUTEX *******/ mReputations.erase(*it) ; updated = true ; } + { + RsStackMutex stack(mReputationMtx); /****** LOCKED MUTEX *******/ + + for(std::map::iterator it(mBannedPgpIds.begin());it!=mBannedPgpIds.end();) + if(it->second.last_activity_TS + BANNED_NODES_INACTIVITY_KEEP < now) + { +#ifdef DEBUG_REPUTATION + std::cerr << " Removing all info about banned node " << it->first << " by lack of activity." << std::endl; +#endif + std::map::iterator tmp(it ) ; + ++tmp ; + mBannedPgpIds.erase(it) ; + it = tmp ; + + updated = true ; + } + else + ++it ; + } + if(updated) IndicateConfigChanged() ; } @@ -762,37 +777,63 @@ bool p3GxsReputation::updateLatestUpdate(RsPeerId peerid,time_t latest_update) * Opinion ****/ -bool p3GxsReputation::getReputationInfo(const RsGxsId& gxsid, const RsPgpId& owner_id, RsReputations::ReputationInfo& info) +bool p3GxsReputation::getReputationInfo(const RsGxsId& gxsid, const RsPgpId& ownerNode, RsReputations::ReputationInfo& info) { if(gxsid.isNull()) return false ; + time_t now = time(NULL) ; + RsStackMutex stack(mReputationMtx); /****** LOCKED MUTEX *******/ -#ifdef DEBUG_REPUTATION +#ifdef DEBUG_REPUTATION2 std::cerr << "getReputationInfo() for " << gxsid << std::endl; #endif - std::map::const_iterator it = mReputations.find(gxsid) ; + std::map::iterator it = mReputations.find(gxsid) ; + RsPgpId owner_id ; if(it == mReputations.end()) { info.mOwnOpinion = RsReputations::OPINION_NEUTRAL ; info.mOverallReputationScore = RsReputations::REPUTATION_THRESHOLD_DEFAULT ; - info.mFriendAverage = REPUTATION_THRESHOLD_DEFAULT ; + info.mFriendAverage = REPUTATION_THRESHOLD_DEFAULT ; + + owner_id = ownerNode ; } else { - const Reputation& rep(it->second) ; + Reputation& rep(it->second) ; info.mOwnOpinion = RsReputations::Opinion(rep.mOwnOpinion) ; info.mOverallReputationScore = rep.mReputation ; info.mFriendAverage = rep.mFriendAverage ; + + if(rep.mOwnerNode.isNull()) + rep.mOwnerNode = ownerNode ; + + owner_id = rep.mOwnerNode ; } - if(!owner_id.isNull() && (mBannedPgpIds.find(owner_id) != mBannedPgpIds.end())) + std::map::iterator it2 ; + + if(!owner_id.isNull() && (it2 = mBannedPgpIds.find(owner_id))!=mBannedPgpIds.end()) { -#ifdef DEBUG_REPUTATION - std::cerr << "p3GxsReputations: identity " << gxsid << " is banned because owner node ID " << owner_id << " is banned." << std::endl; + if(it2->second.known_identities.find(gxsid) == it2->second.known_identities.end()) + { + it2->second.known_identities.insert(gxsid) ; + it2->second.last_activity_TS = now ; + mBannedNodesProxyNeedsUpdate = true ; + } + + info.mAssessment = RsReputations::ASSESSMENT_BAD ; +#ifdef DEBUG_REPUTATION2 + std::cerr << "p3GxsReputations: identity " << gxsid << " is banned because owner node ID " << owner_id << " is banned (found in banned nodes list)." << std::endl; +#endif + } + else if(mPerNodeBannedIdsProxy.find(gxsid) != mPerNodeBannedIdsProxy.end()) + { +#ifdef DEBUG_REPUTATION2 + std::cerr << "p3GxsReputations: identity " << gxsid << " is banned because owner node ID " << owner_id << " is banned (found in proxy)." << std::endl; #endif info.mAssessment = RsReputations::ASSESSMENT_BAD ; } @@ -801,18 +842,46 @@ bool p3GxsReputation::getReputationInfo(const RsGxsId& gxsid, const RsPgpId& own else info.mAssessment = RsReputations::ASSESSMENT_OK ; -#ifdef DEBUG_REPUTATION - std::cerr << " information present. OwnOp = " << info.mOwnOpinion << ", overall score=" << info.mAssessment << std::endl; +#ifdef DEBUG_REPUTATION2 + std::cerr << " information present. OwnOp = " << info.mOwnOpinion << ", owner node=" << owner_id << ", overall score=" << info.mAssessment << std::endl; #endif return true ; } -bool p3GxsReputation::isIdentityBanned(const RsGxsId &id,const RsPgpId& owner_node) +void p3GxsReputation::banNode(const RsPgpId& id,bool b) +{ + RsStackMutex stack(mReputationMtx); /****** LOCKED MUTEX *******/ + + if(b) + { + if(mBannedPgpIds.find(id) == mBannedPgpIds.end()) + { + mBannedPgpIds[id] = BannedNodeInfo() ; + IndicateConfigChanged(); + } + } + else + { + if(mBannedPgpIds.find(id) != mBannedPgpIds.end()) + { + mBannedPgpIds.erase(id) ; + IndicateConfigChanged(); + } + } +} +bool p3GxsReputation::isNodeBanned(const RsPgpId& id) +{ + RsStackMutex stack(mReputationMtx); /****** LOCKED MUTEX *******/ + + return mBannedPgpIds.find(id) != mBannedPgpIds.end(); +} + +bool p3GxsReputation::isIdentityBanned(const RsGxsId &id) { RsReputations::ReputationInfo info ; - if(!getReputationInfo(id,owner_node,info)) + if(!getReputationInfo(id,RsPgpId(),info)) return false ; #ifdef DEBUG_REPUTATION @@ -943,12 +1012,23 @@ bool p3GxsReputation::saveList(bool& cleanup, std::list &savelist) savelist.push_back(item); count++; } + + for(std::map::const_iterator it(mBannedPgpIds.begin());it!=mBannedPgpIds.end();++it) + { + RsGxsReputationBannedNodeSetItem *item = new RsGxsReputationBannedNodeSetItem(); + + item->mPgpId = it->first ; + item->mLastActivityTS = it->second.last_activity_TS; + item->mKnownIdentities.ids = it->second.known_identities; + + savelist.push_back(item) ; + } RsConfigKeyValueSet *vitem = new RsConfigKeyValueSet ; RsTlvKeyValue kv; - kv.key = "AUTO_BAN_NODES_THRESHOLD" ; - rs_sprintf(kv.value, "%d", mPgpAutoBanThreshold); - vitem->tlvkvs.pairs.push_back(kv) ; +// kv.key = "AUTO_BAN_NODES_THRESHOLD" ; +// rs_sprintf(kv.value, "%d", mPgpAutoBanThreshold); +// vitem->tlvkvs.pairs.push_back(kv) ; kv.key = "AUTO_BAN_IDENTITIES_THRESHOLD" ; rs_sprintf(kv.value, "%f", mAutoBanIdentitiesLimit); @@ -971,7 +1051,7 @@ void p3GxsReputation::saveDone() bool p3GxsReputation::loadList(std::list& loadList) { #ifdef DEBUG_REPUTATION - std::cerr << "p3GxsReputation::saveList()" << std::endl; + std::cerr << "p3GxsReputation::loadList()" << std::endl; #endif std::list::iterator it; std::set peerSet; @@ -990,28 +1070,39 @@ bool p3GxsReputation::loadList(std::list& loadList) config.mLatestUpdate = item->mLatestUpdate; config.mLastQuery = 0; - peerSet.insert(peerId); + peerSet.insert(peerId); } RsGxsReputationSetItem *set = dynamic_cast(*it); + if (set) loadReputationSet(set, peerSet); + + RsGxsReputationBannedNodeSetItem *itm2 = dynamic_cast(*it) ; + + if(itm2 != NULL) + { + BannedNodeInfo& info(mBannedPgpIds[itm2->mPgpId]) ; + info.last_activity_TS = itm2->mLastActivityTS ; + info.known_identities = itm2->mKnownIdentities.ids ; + } + RsConfigKeyValueSet *vitem = dynamic_cast(*it); if(vitem) for(std::list::const_iterator kit = vitem->tlvkvs.pairs.begin(); kit != vitem->tlvkvs.pairs.end(); ++kit) { - if(kit->key == "AUTO_BAN_NODES_THRESHOLD") - { - int val ; - if (sscanf(kit->value.c_str(), "%d", &val) == 1) - { - mPgpAutoBanThreshold = val ; - std::cerr << "Setting AutoBanNode threshold to " << val << std::endl ; - mLastBannedNodesUpdate = 0 ; // force update - } - }; +// if(kit->key == "AUTO_BAN_NODES_THRESHOLD") +// { +// int val ; +// if (sscanf(kit->value.c_str(), "%d", &val) == 1) +// { +// mPgpAutoBanThreshold = val ; +// std::cerr << "Setting AutoBanNode threshold to " << val << std::endl ; +// mLastBannedNodesUpdate = 0 ; // force update +// } +// }; if(kit->key == "AUTO_BAN_IDENTITIES_THRESHOLD") { float val ; @@ -1034,53 +1125,62 @@ bool p3GxsReputation::loadList(std::list& loadList) delete (*it); } + updateBannedNodesProxy(); loadList.clear() ; return true; } bool p3GxsReputation::loadReputationSet(RsGxsReputationSetItem *item, const std::set &peerSet) { - RsStackMutex stack(mReputationMtx); /****** LOCKED MUTEX *******/ + { + RsStackMutex stack(mReputationMtx); /****** LOCKED MUTEX *******/ - std::map::iterator rit; + std::map::iterator rit; - if(item->mGxsId.isNull()) // just a protection against potential errors having put 00000 into ids. + if(item->mGxsId.isNull()) // just a protection against potential errors having put 00000 into ids. return false ; - - /* find matching Reputation */ - RsGxsId gxsId(item->mGxsId); - rit = mReputations.find(gxsId); - if (rit != mReputations.end()) - { - std::cerr << "ERROR"; - std::cerr << std::endl; - } - Reputation &reputation = mReputations[gxsId]; + /* find matching Reputation */ + RsGxsId gxsId(item->mGxsId); + rit = mReputations.find(gxsId); + if (rit != mReputations.end()) + { + std::cerr << "ERROR"; + std::cerr << std::endl; + } - // install opinions. - std::map::const_iterator oit; - for(oit = item->mOpinions.begin(); oit != item->mOpinions.end(); ++oit) - { - // expensive ... but necessary. - RsPeerId peerId(oit->first); - if (peerSet.end() != peerSet.find(peerId)) - reputation.mOpinions[peerId] = safe_convert_uint32t_to_opinion(oit->second); - } + Reputation &reputation = mReputations[gxsId]; - reputation.mOwnOpinion = item->mOwnOpinion; - reputation.mOwnOpinionTs = item->mOwnOpinionTS; - reputation.mOwnerNode = item->mOwnerNodeId; + // install opinions. + std::map::const_iterator oit; + for(oit = item->mOpinions.begin(); oit != item->mOpinions.end(); ++oit) + { + // expensive ... but necessary. + RsPeerId peerId(oit->first); + if (peerSet.end() != peerSet.find(peerId)) + reputation.mOpinions[peerId] = safe_convert_uint32t_to_opinion(oit->second); + } - // if dropping entries has changed the score -> must update. - - //float old_reputation = reputation.mReputation ; - //mUpdatedReputations.insert(gxsId) ; - - reputation.updateReputation() ; + reputation.mOwnOpinion = item->mOwnOpinion; + reputation.mOwnOpinionTs = item->mOwnOpinionTS; + reputation.mOwnerNode = item->mOwnerNodeId; + reputation.mIdentityFlags = item->mIdentityFlags | REPUTATION_IDENTITY_FLAG_NEEDS_UPDATE; - mUpdated.insert(std::make_pair(reputation.mOwnOpinionTs, gxsId)); - return true; + // if dropping entries has changed the score -> must update. + + //float old_reputation = reputation.mReputation ; + //mUpdatedReputations.insert(gxsId) ; + + reputation.updateReputation() ; + + mUpdated.insert(std::make_pair(reputation.mOwnOpinionTs, gxsId)); + } +#ifdef DEBUG_REPUTATION + RsReputations::ReputationInfo info ; + getReputationInfo(item->mGxsId,item->mOwnerNodeId,info) ; + std::cerr << item->mGxsId << " : own: " << info.mOwnOpinion << ", owner node: " << item->mOwnerNodeId << ", assessment: " << ((info.mAssessment==ASSESSMENT_BAD)?"BAD":"OK") << std::endl; +#endif + return true; } @@ -1260,16 +1360,33 @@ void p3GxsReputation::debug_print() { std::cerr << "Reputations database: " << std::endl; std::cerr << " Average number of peers: " << mAverageActiveFriends << std::endl; - + std::cerr << " GXS ID data: " << std::endl; + time_t now = time(NULL) ; - + for(std::map::const_iterator it(mReputations.begin());it!=mReputations.end();++it) { - std::cerr << " ID=" << it->first << ", own: " << it->second.mOwnOpinion << ", Friend average: " << it->second.mFriendAverage << ", global_score: " << it->second.mReputation - << ", last own update: " << now - it->second.mOwnOpinionTs << " secs ago." << std::endl; - - for(std::map::const_iterator it2(it->second.mOpinions.begin());it2!=it->second.mOpinions.end();++it2) + std::cerr << " " << it->first << ": own: " << it->second.mOwnOpinion << ", Friend average: " << it->second.mFriendAverage << ", global_score: " << it->second.mReputation + << ", last own update: " << now - it->second.mOwnOpinionTs << " secs ago." << std::endl; +#ifdef DEBUG_REPUTATION2 + for(std::map::const_iterator it2(it->second.mOpinions.begin());it2!=it->second.mOpinions.end();++it2) std::cerr << " " << it2->first << ": " << it2->second << std::endl; +#endif } + + std::cerr << " Banned RS nodes by ID: " << std::endl; + + for(std::map::const_iterator it(mBannedPgpIds.begin());it!=mBannedPgpIds.end();++it) + { + std::cerr << " Node " << it->first << ", last activity: " << now - it->second.last_activity_TS << " secs ago." << std::endl; + + for(std::set::const_iterator it2(it->second.known_identities.begin());it2!=it->second.known_identities.end();++it2) + std::cerr << " " << *it2 << std::endl; + } + + std::cerr << " Per node Banned GXSIds proxy: " << std::endl; + + for(std::set::const_iterator it(mPerNodeBannedIdsProxy.begin());it!=mPerNodeBannedIdsProxy.end();++it) + std::cerr << " " << *it << std::endl; } diff --git a/libretroshare/src/services/p3gxsreputation.h b/libretroshare/src/services/p3gxsreputation.h index 7bed661b2..94fb356e1 100644 --- a/libretroshare/src/services/p3gxsreputation.h +++ b/libretroshare/src/services/p3gxsreputation.h @@ -59,6 +59,12 @@ public: time_t mLastQuery; }; +struct BannedNodeInfo +{ + time_t last_activity_TS ; // updated everytime a node or one of its former identities is required + std::set known_identities ; // list of known identities from this node. This is kept for a while, and useful in order to avoid re-asking these keys. +}; + class Reputation { public: @@ -77,7 +83,7 @@ public: float mFriendAverage ; float mReputation; - RsPgpId mOwnerNode; + RsPgpId mOwnerNode; uint32_t mIdentityFlags; }; @@ -97,11 +103,15 @@ public: /***** Interface for RsReputations *****/ virtual bool setOwnOpinion(const RsGxsId& key_id, const Opinion& op) ; - virtual bool getReputationInfo(const RsGxsId& id, const RsPgpId &owner_id, ReputationInfo& info) ; - virtual bool isIdentityBanned(const RsGxsId& id, const RsPgpId &owner_node) ; + virtual bool getReputationInfo(const RsGxsId& id, const RsPgpId &ownerNode, ReputationInfo& info) ; + virtual bool isIdentityBanned(const RsGxsId& id) ; + + virtual bool isNodeBanned(const RsPgpId& id); + virtual void banNode(const RsPgpId& id,bool b) ; + + //virtual void setNodeAutoBanThreshold(uint32_t n) ; + //virtual uint32_t nodeAutoBanThreshold() ; - virtual void setNodeAutoBanThreshold(uint32_t n) ; - virtual uint32_t nodeAutoBanThreshold() ; virtual void setNodeAutoPositiveOpinionForContacts(bool b) ; virtual bool nodeAutoPositiveOpinionForContacts() ; virtual float nodeAutoBanIdentitiesLimit() ; @@ -129,7 +139,8 @@ private: bool RecvReputations(RsGxsReputationUpdateItem *item); bool updateLatestUpdate(RsPeerId peerid, time_t latest_update); void updateActiveFriends() ; - void updateBannedNodesList(); + + void updateBannedNodesProxy(); // internal update of data. Takes care of cleaning empty boxes. void locked_updateOpinion(const RsPeerId &from, const RsGxsId &about, RsReputations::Opinion op); @@ -167,8 +178,10 @@ private: std::set mUpdatedReputations; // PGP Ids auto-banned. This is updated regularly. - std::set mBannedPgpIds ; - uint32_t mPgpAutoBanThreshold ; + std::map mBannedPgpIds ; + std::set mPerNodeBannedIdsProxy ; + //uint32_t mPgpAutoBanThreshold ; + bool mBannedNodesProxyNeedsUpdate ; }; #endif //SERVICE_RSGXSREPUTATION_HEADER diff --git a/libretroshare/src/services/p3idservice.cc b/libretroshare/src/services/p3idservice.cc index 0e4062520..a20efbaf6 100644 --- a/libretroshare/src/services/p3idservice.cc +++ b/libretroshare/src/services/p3idservice.cc @@ -51,16 +51,16 @@ * #define GXSID_GEN_DUMMY_DATA 1 ****/ -#define ID_REQUEST_LIST 0x0001 -#define ID_REQUEST_IDENTITY 0x0002 +#define ID_REQUEST_LIST 0x0001 +#define ID_REQUEST_IDENTITY 0x0002 #define ID_REQUEST_REPUTATION 0x0003 -#define ID_REQUEST_OPINION 0x0004 +#define ID_REQUEST_OPINION 0x0004 #define GXSID_MAX_CACHE_SIZE 5000 // unused keys are deleted according to some heuristic that should favor known keys, signed keys etc. -static const time_t MAX_KEEP_KEYS_BANNED = 2 * 86400 ; // get rid of banned ids after 2 days. That gives a chance to un-ban someone before he gets kicked out +static const time_t MAX_KEEP_KEYS_BANNED = 1 * 86400 ; // get rid of banned ids after 1 days. That gives a chance to un-ban someone before he gets definitely kicked out static const time_t MAX_KEEP_KEYS_DEFAULT = 5 * 86400 ; // default for unsigned identities: 5 days static const time_t MAX_KEEP_KEYS_SIGNED = 8 * 86400 ; // signed identities by unknown key static const time_t MAX_KEEP_KEYS_SIGNED_KNOWN = 30 * 86400 ; // signed identities by known node keys @@ -324,7 +324,7 @@ public: time_t now = time(NULL); const RsGxsId& gxs_id = entry.details.mId ; - bool is_id_banned = rsReputations->isIdentityBanned(gxs_id,entry.details.mPgpId) ; + bool is_id_banned = rsReputations->isIdentityBanned(gxs_id) ; bool is_own_id = (bool)(entry.details.mFlags & RS_IDENTITY_FLAGS_IS_OWN_ID) ; bool is_known_id = (bool)(entry.details.mFlags & RS_IDENTITY_FLAGS_PGP_KNOWN) ; bool is_signed_id = (bool)(entry.details.mFlags & RS_IDENTITY_FLAGS_PGP_LINKED) ; @@ -739,6 +739,18 @@ bool p3IdService::requestKey(const RsGxsId &id, const std::list& peers return true; else { + // Normally we should call getIdDetails(), but since the key is not known, we need to digg a possibly old information + // from the reputation system, which keeps its own list of banned keys. Of course, the owner ID is not known at this point. + + RsReputations::ReputationInfo info ; + rsReputations->getReputationInfo(id,RsPgpId(),info) ; + + if(info.mAssessment == RsReputations::ASSESSMENT_BAD) + { + std::cerr << "(II) not requesting Key " << id << " because it has been banned." << std::endl; + return true; + } + RsStackMutex stack(mIdMtx); /********** STACK LOCKED MTX ******/ std::map >::iterator rit = mIdsNotPresent.find(id) ; diff --git a/retroshare-gui/src/gui/Identity/IdDialog.cpp b/retroshare-gui/src/gui/Identity/IdDialog.cpp index 24b4e33c4..bfcb3a54a 100644 --- a/retroshare-gui/src/gui/Identity/IdDialog.cpp +++ b/retroshare-gui/src/gui/Identity/IdDialog.cpp @@ -333,6 +333,7 @@ IdDialog::IdDialog(QWidget *parent) : //connect(ui->treeWidget_membership, SIGNAL(itemSelectionChanged()), this, SLOT(circle_selected())); connect(ui->treeWidget_membership, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(CircleListCustomPopupMenu(QPoint))); + connect(ui->autoBanIdentities_CB, SIGNAL(toggled(bool)), this, SLOT(toggleAutoBanIdentities(bool))); /* Setup TokenQueue */ @@ -349,6 +350,17 @@ IdDialog::IdDialog(QWidget *parent) : tmer->start(10000) ; // update every 10 secs. } +void IdDialog::toggleAutoBanIdentities(bool b) +{ + RsPgpId id(ui->lineEdit_GpgId->text().left(16).toStdString()); + + if(!id.isNull()) + { + rsReputations->banNode(id,b) ; + requestIdList(); + } +} + void IdDialog::updateCirclesDisplay() { if(RsAutoUpdatePage::eventsLocked()) @@ -1676,6 +1688,8 @@ void IdDialog::insertIdDetails(uint32_t token) else ui->lineEdit_GpgId->setText(QString::fromStdString(data.mPgpId.toStdString()) + tr(" [unverified]")); + ui->autoBanIdentities_CB->setVisible(!data.mPgpId.isNull()) ; + time_t now = time(NULL) ; ui->lineEdit_LastUsed->setText(getHumanReadableDuration(now - data.mLastUsageTS)) ; ui->headerTextLabel_Person->setText(QString::fromUtf8(data.mMeta.mGroupName.c_str()).left(RSID_MAXIMUM_NICKNAME_SIZE)); @@ -1769,6 +1783,8 @@ void IdDialog::insertIdDetails(uint32_t token) ui->inviteButton->setEnabled(true); } + ui->autoBanIdentities_CB->setChecked(rsReputations->isNodeBanned(data.mPgpId)); + /* now fill in the reputation information */ #ifdef SUSPENDED diff --git a/retroshare-gui/src/gui/Identity/IdDialog.h b/retroshare-gui/src/gui/Identity/IdDialog.h index 4253ddb9c..2c4a2d9ea 100644 --- a/retroshare-gui/src/gui/Identity/IdDialog.h +++ b/retroshare-gui/src/gui/Identity/IdDialog.h @@ -77,6 +77,7 @@ private slots: void createExternalCircle(); void showEditExistingCircle(); void updateCirclesDisplay(); + void toggleAutoBanIdentities(bool b); void acceptCircleSubscription() ; void cancelCircleSubscription() ; diff --git a/retroshare-gui/src/gui/Identity/IdDialog.ui b/retroshare-gui/src/gui/Identity/IdDialog.ui index 42347f116..234d88a35 100644 --- a/retroshare-gui/src/gui/Identity/IdDialog.ui +++ b/retroshare-gui/src/gui/Identity/IdDialog.ui @@ -7,7 +7,7 @@ 0 0 800 - 600 + 872 @@ -117,7 +117,7 @@ Qt::NoFocus - + :/icons/help_64.png:/icons/help_64.png @@ -278,7 +278,7 @@ Reputation - AlignLeading|AlignVCenter + AlignLeft|AlignVCenter @@ -534,6 +534,13 @@ Reputation + + + + Auto-Ban all identities from this node + + + @@ -806,8 +813,8 @@ p, li { white-space: pre-wrap; } idTreeWidget - + diff --git a/retroshare-gui/src/gui/settings/PeoplePage.cpp b/retroshare-gui/src/gui/settings/PeoplePage.cpp index 79959c398..6ce71233f 100644 --- a/retroshare-gui/src/gui/settings/PeoplePage.cpp +++ b/retroshare-gui/src/gui/settings/PeoplePage.cpp @@ -37,11 +37,6 @@ PeoplePage::~PeoplePage() /** Saves the changes on this page */ bool PeoplePage::save(QString &/*errmsg*/) { - if(!ui.identityBan_CB->isChecked()) - rsReputations->setNodeAutoBanThreshold(0) ; - else - rsReputations->setNodeAutoBanThreshold(ui.identityBanThreshold_SB->value()) ; - if(ui.autoPositiveOpinion_CB->isChecked()) rsReputations->setNodeAutoPositiveOpinionForContacts(true) ; else @@ -55,12 +50,9 @@ bool PeoplePage::save(QString &/*errmsg*/) /** Loads the settings for this page */ void PeoplePage::load() { - uint32_t ban_limit = rsReputations->nodeAutoBanThreshold() ; bool auto_positive_contacts = rsReputations->nodeAutoPositiveOpinionForContacts() ; float node_auto_ban_identities_limit = rsReputations->nodeAutoBanIdentitiesLimit(); - ui.identityBan_CB->setChecked(ban_limit > 0) ; - ui.identityBanThreshold_SB->setValue(ban_limit) ; ui.autoPositiveOpinion_CB->setChecked(auto_positive_contacts); ui.autoBanIdentitiesLimit_SB->setValue(node_auto_ban_identities_limit); } diff --git a/retroshare-gui/src/gui/settings/PeoplePage.ui b/retroshare-gui/src/gui/settings/PeoplePage.ui index 31d05588f..08a5c4967 100644 --- a/retroshare-gui/src/gui/settings/PeoplePage.ui +++ b/retroshare-gui/src/gui/settings/PeoplePage.ui @@ -17,50 +17,6 @@ Identities handling - - - - - - ban all identities of a node when more than - - - true - - - - - - - 10 - - - 2 - - - - - - - of them have a negative opinion - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - -