def get_closest(self, hash, count=None): """ Gets the closest n contacts to a hash. :param hash: The hash to compare to. :type hash: :class:`common.Hash` :param count: Number of contacts to return. :type count: int. """ if count is None: count = self.K targethash = (self.own_hash ^ hash) significant_bit = targethash.significant_bit() contacts = list() # Sorted Indices (for prox to the contact). # Ignore the one that is its own bucket to avoid any recursion. si = sorted([x for x in range(1, self.B + 1)], key=lambda x: abs(x - significant_bit)) for dindex in si: contacts += self._buckets[dindex].contacts if (len(contacts) >= count): return sorted(contacts, key=lambda x: targethash.AbsDiff(self.own_hash ^ x.hash))[:count] Logger.debug("GET_CLOSEST ret: %s" % contacts) return contacts
def rm_search(self, addr): """ Filters all searches to remove the one given to it. :param addr: The address to remove. :type addr: :class:`common.Address` """ self.in_progress = list(filter(lambda x: x.contact.address != addr, self.in_progress))
def rm_search(self, addr): """ Filters all searches to remove the one given to it. :param addr: The address to remove. :type addr: :class:`common.Address` """ self.in_progress = list( filter(lambda x: x.contact.address != addr, self.in_progress))
def translate(self, address): """ Translates an :class:`~common.Address` a :class:`~common.Contact`. If no matching contact exists, one is created. :param address: The address data was received from. :type address: :class:`~common.Address` :param hash_: Given if this is a new client (on 'hello') :type hash_: :class:`~common.Hash` """ # Get an existing contact try: contact = self._contacts_by_addr[str(address)] # Set the last time seen (to now) contact.last_seen = datetime.now() # Try associating the contact with a friend if not contact.needs_hash and not contact.has_friend: try: self._friends[contact.hash.value].associate(contact) except KeyError: pass # Errors if the contact does not exist except KeyError: contact = Contact(address) contact.channels['bytelynx'].crypto.p = self._dh_p contact.on_hash += self.on_contact_hash contact.on_death += self.clean_contact self._contacts_by_addr[str(address)] = contact self._on_changed() # Check for a sweep # TODO: Do we need to sweep contacts_by_hash? if datetime.now() - self._last_check > \ timedelta(minutes=SWEEP_INTERVAL): self._last_check = datetime.now() del_time = datetime.now() - timedelta(minutes=EXPIRE_TIME) del_list = list(self._contacts_by_addr.values())\ .where(lambda x: x.last_seen < del_time) for item in del_list: item.on_death() return contact
def __init__(self, own_hash, target_hash, initial_contacts, K): """ :param target_hash: The :hash that this shortlist is seaching for. :type target_hash: :class:`common.Hash` :param initial_contacts: Contacts to start the shorlist with. :type initial_contacts: [:class:`common.Contact`] """ self.K = K self.search_space = list(SearchContact(contacted=False, contact=x) for x in initial_contacts) self.own_hash = own_hash self.target_hash = target_hash self.on_full_or_found = Event('Shortlist.on_full_or_found') self.in_progress = [] # Ensure we start off increasing. self.sort() self._closest = {} # Add the closest contact (our vote) # This only matters if there is 2 nodes c = self.find_min() if c is not None: self._add_closest(c.contact)
def _clean_lists(self): for hash_, shortlist in self._shortlists.items(): # Magic Number [1000]: convert seconds to ms # Magic Number [5]: tweakable to set how long to timeout requests alive = list(filter(lambda x: (datetime.now()-x.time).total_seconds() * 1000 < x.contact.ping * 5, shortlist.in_progress)) # If any of the requests have timed out. if (len(alive) != len(shortlist.in_progress)): shortlist.in_progress = alive # Add any needed more requests to reach the parallel param A. while (len(shortlist.in_progress) < self.A): next_min = shortlist.get_next() # We have no more useable responses. if (next_min is None): # No searches are in progress. if (len(shortlist.in_progress) == 0): shortlist.on_full_or_found(shortlist.target_hash, shortlist.closest) break else: self.on_search(hash_, next_min)
def __init__(self, own_hash, target_hash, initial_contacts, K): """ :param target_hash: The :hash that this shortlist is seaching for. :type target_hash: :class:`common.Hash` :param initial_contacts: Contacts to start the shorlist with. :type initial_contacts: [:class:`common.Contact`] """ self.K = K self.search_space = list( SearchContact(contacted=False, contact=x) for x in initial_contacts) self.own_hash = own_hash self.target_hash = target_hash self.on_full_or_found = Event('Shortlist.on_full_or_found') self.in_progress = [] # Ensure we start off increasing. self.sort() self._closest = {} # Add the closest contact (our vote) # This only matters if there is 2 nodes c = self.find_min() if c is not None: self._add_closest(c.contact)
def _clean_lists(self): for hash_, shortlist in self._shortlists.items(): # Magic Number [1000]: convert seconds to ms # Magic Number [5]: tweakable to set how long to timeout requests alive = list( filter( lambda x: (datetime.now() - x.time).total_seconds() * 1000 < x.contact.ping * 5, shortlist.in_progress)) # If any of the requests have timed out. if (len(alive) != len(shortlist.in_progress)): shortlist.in_progress = alive # Add any needed more requests to reach the parallel param A. while (len(shortlist.in_progress) < self.A): next_min = shortlist.get_next() # We have no more useable responses. if (next_min is None): # No searches are in progress. if (len(shortlist.in_progress) == 0): shortlist.on_full_or_found(shortlist.target_hash, shortlist.closest) break else: self.on_search(hash_, next_min)
def __init__(self, K): self.K = K self.contacts = list() self.waitlist = list() self.on_added = Event('Bucket.on_added') self.on_removed = Event('Bucket.on_removed')
def friends(self): return list(self._friends.values())
def net_contacts(self): return list(self._contacts_by_addr.values())