lbry-sdk/lbrynet/dht/kbucket.py

152 lines
5.2 KiB
Python
Raw Normal View History

2018-05-23 23:42:49 +02:00
import logging
2018-07-18 02:57:02 +02:00
from binascii import hexlify
from . import constants
from .distance import Distance
from .error import BucketFull
2018-07-18 02:57:02 +02:00
import sys
if sys.version_info > (3,):
long = int
2015-08-20 17:27:15 +02:00
2018-05-23 23:42:49 +02:00
log = logging.getLogger(__name__)
2015-08-20 17:27:15 +02:00
class KBucket:
2015-08-20 17:27:15 +02:00
""" Description - later
"""
2017-03-31 19:32:43 +02:00
2018-05-23 23:06:45 +02:00
def __init__(self, rangeMin, rangeMax, node_id):
2015-08-20 17:27:15 +02:00
"""
@param rangeMin: The lower boundary for the range in the n-bit ID
space covered by this k-bucket
@param rangeMax: The upper boundary for the range in the ID space
covered by this k-bucket
"""
self.lastAccessed = 0
self.rangeMin = rangeMin
self.rangeMax = rangeMax
self._contacts = list()
2018-05-23 23:06:45 +02:00
self._node_id = node_id
2015-08-20 17:27:15 +02:00
def addContact(self, contact):
""" Add contact to _contact list in the right order. This will move the
contact to the end of the k-bucket if it is already present.
2016-12-14 00:08:29 +01:00
2015-08-20 17:27:15 +02:00
@raise kademlia.kbucket.BucketFull: Raised when the bucket is full and
the contact isn't in the bucket
already
2016-12-14 00:08:29 +01:00
2015-08-20 17:27:15 +02:00
@param contact: The contact to add
2018-05-23 23:42:49 +02:00
@type contact: dht.contact._Contact
2015-08-20 17:27:15 +02:00
"""
if contact in self._contacts:
# Move the existing contact to the end of the list
2016-11-30 21:20:45 +01:00
# - using the new contact to allow add-on data
# (e.g. optimization-specific stuff) to pe updated as well
2015-08-20 17:27:15 +02:00
self._contacts.remove(contact)
self._contacts.append(contact)
elif len(self._contacts) < constants.k:
self._contacts.append(contact)
else:
raise BucketFull("No space in bucket to insert contact")
def getContact(self, contactID):
"""Get the contact specified node ID
@raise IndexError: raised if the contact is not in the bucket
@param contactID: the node id of the contact to retrieve
@type contactID: str
@rtype: dht.contact._Contact
"""
for contact in self._contacts:
if contact.id == contactID:
return contact
raise IndexError(contactID)
2015-08-20 17:27:15 +02:00
2018-05-23 23:42:49 +02:00
def getContacts(self, count=-1, excludeContact=None, sort_distance_to=None):
2015-08-20 17:27:15 +02:00
""" Returns a list containing up to the first count number of contacts
2016-12-14 00:08:29 +01:00
2015-08-20 17:27:15 +02:00
@param count: The amount of contacts to return (if 0 or less, return
all contacts)
@type count: int
2018-05-23 23:42:49 +02:00
@param excludeContact: A node id to exclude; if this contact is in
2015-08-20 17:27:15 +02:00
the list of returned values, it will be
discarded before returning. If a C{str} is
passed as this argument, it must be the
2016-12-14 00:08:29 +01:00
contact's ID.
2018-05-23 23:42:49 +02:00
@type excludeContact: str
2016-12-14 00:08:29 +01:00
2018-05-23 23:42:49 +02:00
@param sort_distance_to: Sort distance to the id, defaulting to the parent node id. If False don't
sort the contacts
2016-12-14 00:08:29 +01:00
2015-08-20 17:27:15 +02:00
@raise IndexError: If the number of requested contacts is too large
2016-12-14 00:08:29 +01:00
2015-08-20 17:27:15 +02:00
@return: Return up to the first count number of contacts in a list
If no contacts are present an empty is returned
@rtype: list
"""
2018-05-23 23:42:49 +02:00
contacts = [contact for contact in self._contacts if contact.id != excludeContact]
2015-08-20 17:27:15 +02:00
# Return all contacts in bucket
if count <= 0:
2018-05-23 23:42:49 +02:00
count = len(contacts)
2015-08-20 17:27:15 +02:00
# Get current contact number
2018-05-23 23:42:49 +02:00
currentLen = len(contacts)
2015-08-20 17:27:15 +02:00
# If count greater than k - return only k contacts
if count > constants.k:
count = constants.k
if not currentLen:
2018-05-23 23:42:49 +02:00
return contacts
2015-08-20 17:27:15 +02:00
2018-05-23 23:42:49 +02:00
if sort_distance_to is False:
pass
2015-08-20 17:27:15 +02:00
else:
2018-05-23 23:42:49 +02:00
sort_distance_to = sort_distance_to or self._node_id
contacts.sort(key=lambda c: Distance(sort_distance_to)(c.id))
2016-12-14 00:08:29 +01:00
2018-05-23 23:42:49 +02:00
return contacts[:min(currentLen, count)]
2015-08-20 17:27:15 +02:00
def getBadOrUnknownContacts(self):
contacts = self.getContacts(sort_distance_to=False)
results = [contact for contact in contacts if contact.contact_is_good is False]
results.extend(contact for contact in contacts if contact.contact_is_good is None)
return results
2015-08-20 17:27:15 +02:00
def removeContact(self, contact):
""" Remove the contact from the bucket
2016-12-14 00:08:29 +01:00
@param contact: The contact to remove
@type contact: dht.contact._Contact
2016-12-14 00:08:29 +01:00
2015-08-20 17:27:15 +02:00
@raise ValueError: The specified contact is not in this bucket
"""
self._contacts.remove(contact)
2016-12-14 00:08:29 +01:00
2015-08-20 17:27:15 +02:00
def keyInRange(self, key):
""" Tests whether the specified key (i.e. node ID) is in the range
of the n-bit ID space covered by this k-bucket (in otherwords, it
returns whether or not the specified key should be placed in this
k-bucket)
2016-12-14 00:08:29 +01:00
2015-08-20 17:27:15 +02:00
@param key: The key to test
@type key: str or int
2016-12-14 00:08:29 +01:00
2015-08-20 17:27:15 +02:00
@return: C{True} if the key is in this k-bucket's range, or C{False}
if not.
@rtype: bool
"""
2018-07-18 02:57:02 +02:00
if isinstance(key, bytes):
key = long(hexlify(key), 16)
2015-08-20 17:27:15 +02:00
return self.rangeMin <= key < self.rangeMax
def __len__(self):
return len(self._contacts)
def __contains__(self, item):
return item in self._contacts