/* * libretroshare/src/services: groutermatrix.cc * * Services for RetroShare. * * Copyright 2013 by Cyril Soler * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License Version 2 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA. * * Please report all bugs and problems to "csoler@users.sourceforge.net". * */ #include "groutertypes.h" #include "groutermatrix.h" #include "grouteritems.h" //#define ROUTING_MATRIX_DEBUG GRouterMatrix::GRouterMatrix() { _proba_need_updating = true ; } bool GRouterMatrix::addTrackingInfo(const RsGxsMessageId& mid,const RsPeerId& source_friend) { time_t now = time(NULL) ; RoutingTrackEntry rte ; rte.friend_id = source_friend ; rte.time_stamp = now ; _tracking_clues[mid] = rte ; #ifdef ROUTING_MATRIX_DEBUG std::cerr << "GRouterMatrix::addTrackingInfo(): Added clue mid=" << mid << ", from " << source_friend << " ID=" << source_friend << std::endl; #endif return true ; } bool GRouterMatrix::cleanUp() { // remove all tracking entries that have become too old. #ifdef ROUTING_MATRIX_DEBUG std::cerr << "GRouterMatrix::cleanup()" << std::endl; #endif time_t now = time(NULL) ; for(std::map::iterator it(_tracking_clues.begin());it!=_tracking_clues.end();) if(it->second.time_stamp + RS_GROUTER_MAX_KEEP_TRACKING_CLUES < now) { #ifdef ROUTING_MATRIX_DEBUG std::cerr << " removing old entry msgId=" << it->first << ", from id " << it->second.friend_id << ", obtained " << (now - it->second.time_stamp) << " secs ago." << std::endl; #endif std::map::iterator tmp(it) ; ++tmp ; _tracking_clues.erase(it) ; it=tmp ; } else ++it ; return true ; } bool GRouterMatrix::addRoutingClue(const GRouterKeyId& key_id,const RsPeerId& source_friend,float weight) { // 1 - get the friend index. // uint32_t fid = getFriendId(source_friend) ; // 2 - get the Key map, and add the routing clue. // time_t now = time(NULL) ; RoutingMatrixHitEntry rc ; rc.weight = weight ; rc.time_stamp = now ; rc.friend_id = fid ; std::list& lst( _routing_clues[key_id] ) ; // Prevent flooding. Happens in two scenarii: // 1 - a user restarts RS very often => keys get republished for some reason // 2 - a user intentionnaly floods a key // // Solution is to look for all recorded events, and not add any new event if an event came from the same friend // too close in the past. Going through the list is not costly since it is bounded to RS_GROUTER_MATRIX_MAX_HIT_ENTRIES elemts. for(std::list::const_iterator mit(lst.begin());mit!=lst.end();++mit) if((*mit).friend_id == fid && (*mit).time_stamp + RS_GROUTER_MATRIX_MIN_TIME_BETWEEN_HITS > now) { std::cerr << "GRouterMatrix::addRoutingClue(): too many clues for key " << key_id.toStdString() << " from friend " << source_friend << " in a small interval of " << now - lst.front().time_stamp << " seconds. Flooding?" << std::endl; return false ; } lst.push_front(rc) ; // create it if necessary // Remove older elements // uint32_t sz = lst.size() ; // O(n)! for(uint32_t i=RS_GROUTER_MATRIX_MAX_HIT_ENTRIES;i::const_iterator it = _friend_indices.find(source_friend) ; if(it == _friend_indices.end()) return _reverse_friend_indices.size() ; else return it->second ; } uint32_t GRouterMatrix::getFriendId(const RsPeerId& source_friend) { std::map::const_iterator it = _friend_indices.find(source_friend) ; if(it == _friend_indices.end()) { // add a new friend uint32_t new_id = _reverse_friend_indices.size() ; _reverse_friend_indices.push_back(source_friend) ; _friend_indices[source_friend] = new_id ; return new_id ; } else return it->second ; } void GRouterMatrix::getListOfKnownKeys(std::vector& key_ids) const { key_ids.clear() ; for(std::map >::const_iterator it(_time_combined_hits.begin());it!=_time_combined_hits.end();++it) key_ids.push_back(it->first) ; } bool GRouterMatrix::getTrackingInfo(const RsGxsMessageId& mid, RsPeerId &source_friend) { std::map::const_iterator it = _tracking_clues.find(mid) ; if(it == _tracking_clues.end()) return false ; source_friend = it->second.friend_id; return true ; } void GRouterMatrix::debugDump() const { std::cerr << " Proba needs up: " << _proba_need_updating << std::endl; std::cerr << " Known keys: " << _time_combined_hits.size() << std::endl; std::cerr << " Routing events: " << std::endl; time_t now = time(NULL) ; for(std::map >::const_iterator it(_routing_clues.begin());it!=_routing_clues.end();++it) { std::cerr << " " << it->first.toStdString() << " : " ; for(std::list::const_iterator it2(it->second.begin());it2!=it->second.end();++it2) std::cerr << now - (*it2).time_stamp << " (" << (*it2).friend_id << "," << (*it2).weight << ") " ; std::cerr << std::endl; } std::cerr << " Routing values: " << std::endl; for(std::map >::const_iterator it(_time_combined_hits.begin());it!=_time_combined_hits.end();++it) { std::cerr << " " << it->first.toStdString() << " : " ; for(uint32_t i=0;isecond.size();++i) std::cerr << it->second[i] << " " ; std::cerr << std::endl; } std::cerr << " Tracking clues: " << std::endl; for(std::map::const_iterator it(_tracking_clues.begin());it!=_tracking_clues.end();++it) std::cerr << " " << it->first << ": from " << it->second.friend_id << " " << now - it->second.time_stamp << " secs ago." << std::endl; } bool GRouterMatrix::computeRoutingProbabilities(const GRouterKeyId& key_id, const std::vector& friends, std::vector& probas) const { // Routing probabilities are computed according to routing clues // // For a given key, each friend has a known set of routing clues (time_t, weight) // We combine these to compute a static weight for each friend/key pair. // This is performed in updateRoutingProbabilities() // // Then for a given list of online friends, the weights are computed into probabilities, // that always sum up to 1. // #ifdef ROUTING_MATRIX_DEBUG if(_proba_need_updating) std::cerr << "GRouterMatrix::computeRoutingProbabilities(): matrix is not up to date. Not a real problem, but still..." << std::endl; #endif probas.resize(friends.size(),0.0f) ; float total = 0.0f ; std::map >::const_iterator it2 = _time_combined_hits.find(key_id) ; if(it2 == _time_combined_hits.end()) { // The key is not known. In this case, we return a zero probability for all peers. // float p = 0.0f;//1.0f / friends.size() ; probas.clear() ; probas.resize(friends.size(),p) ; #ifdef ROUTING_MATRIX_DEBUG std::cerr << "GRouterMatrix::computeRoutingProbabilities(): key id " << key_id.toStdString() << " does not exist! Returning uniform probabilities." << std::endl; #endif return false ; } const std::vector& w(it2->second) ; for(uint32_t i=0;i= w.size()) probas[i] = 0.0f ; else { probas[i] = w[findex] ; total += w[findex] ; } } if(total > 0.0f) for(uint32_t i=0;i >::const_iterator it(_routing_clues.begin());it!=_routing_clues.end();++it) { #ifdef ROUTING_MATRIX_DEBUG std::cerr << " " << it->first.toStdString() << " : " ; #endif std::vector& v(_time_combined_hits[it->first]) ; v.clear() ; v.resize(_friend_indices.size(),0.0f) ; for(std::list::const_iterator it2(it->second.begin());it2!=it->second.end();++it2) { // Half life period is 7 days. float time_difference_in_days = 1 + (now - (*it2).time_stamp ) / (7*86400.0f) ; v[(*it2).friend_id] += (*it2).weight / (time_difference_in_days*time_difference_in_days) ; } } #ifdef ROUTING_MATRIX_DEBUG std::cerr << " done." << std::endl; #endif _proba_need_updating = false ; return true ; } bool GRouterMatrix::saveList(std::list& items) { #ifdef ROUTING_MATRIX_DEBUG std::cerr << " GRoutingMatrix::saveList()" << std::endl; #endif RsGRouterMatrixFriendListItem *item = new RsGRouterMatrixFriendListItem ; item->reverse_friend_indices = _reverse_friend_indices ; items.push_back(item) ; for(std::map >::const_iterator it(_routing_clues.begin());it!=_routing_clues.end();++it) { RsGRouterMatrixCluesItem *item = new RsGRouterMatrixCluesItem ; item->destination_key = it->first ; item->clues = it->second ; items.push_back(item) ; } for(std::map::const_iterator it(_tracking_clues.begin());it!=_tracking_clues.end();++it) { RsGRouterMatrixTrackItem *item = new RsGRouterMatrixTrackItem ; item->provider_id = it->second.friend_id ; item->time_stamp = it->second.time_stamp ; item->message_id = it->first ; items.push_back(item) ; } return true ; } bool GRouterMatrix::loadList(std::list& items) { RsGRouterMatrixFriendListItem *itm1 = NULL ; RsGRouterMatrixCluesItem *itm2 = NULL ; RsGRouterMatrixTrackItem *itm3 = NULL ; #ifdef ROUTING_MATRIX_DEBUG std::cerr << " GRoutingMatrix::loadList()" << std::endl; #endif for(std::list::const_iterator it(items.begin());it!=items.end();++it) { if(NULL != (itm3 = dynamic_cast(*it))) { #ifdef ROUTING_MATRIX_DEBUG std::cerr << " initing tracking clues." << std::endl; #endif RoutingTrackEntry rte ; rte.friend_id = itm3->provider_id ; rte.time_stamp = itm3->time_stamp ; _tracking_clues[itm3->message_id] = rte; } if(NULL != (itm2 = dynamic_cast(*it))) { #ifdef ROUTING_MATRIX_DEBUG std::cerr << " initing routing clues." << std::endl; #endif _routing_clues[itm2->destination_key] = itm2->clues ; _proba_need_updating = true ; // notifies to re-compute all the info. } if(NULL != (itm1 = dynamic_cast(*it))) { _reverse_friend_indices = itm1->reverse_friend_indices ; _friend_indices.clear() ; for(uint32_t i=0;i<_reverse_friend_indices.size();++i) _friend_indices[_reverse_friend_indices[i]] = i ; _proba_need_updating = true ; // notifies to re-compute all the info. } } return true ; }