示例#1
0
    def test_invalid_key_length(self):
        self.assertRaises(ValueError, Distance, b'1' * 47)
        self.assertRaises(ValueError, Distance, b'1' * 49)
        self.assertRaises(ValueError, Distance, b'')

        self.assertRaises(ValueError, Distance(b'0' * 48), b'1' * 47)
        self.assertRaises(ValueError, Distance(b'0' * 48), b'1' * 49)
        self.assertRaises(ValueError, Distance(b'0' * 48), b'')
示例#2
0
    def __init__(self, loop: asyncio.BaseEventLoop, peer_manager: 'PeerManager',
                 routing_table: 'TreeRoutingTable', protocol: 'KademliaProtocol', key: bytes,
                 bottom_out_limit: typing.Optional[int] = 2, max_results: typing.Optional[int] = constants.k,
                 exclude: typing.Optional[typing.List[typing.Tuple[str, int]]] = None,
                 shortlist: typing.Optional[typing.List['KademliaPeer']] = None):
        if len(key) != constants.hash_length:
            raise ValueError("invalid key length: %i" % len(key))
        self.loop = loop
        self.peer_manager = peer_manager
        self.routing_table = routing_table
        self.protocol = protocol

        self.key = key
        self.bottom_out_limit = bottom_out_limit
        self.max_results = max_results
        self.exclude = exclude or []

        self.shortlist: typing.List['KademliaPeer'] = get_shortlist(routing_table, key, shortlist)
        self.active: typing.Set['KademliaPeer'] = set()
        self.contacted: typing.Set[typing.Tuple[str, int]] = set()
        self.distance = Distance(key)

        self.closest_peer: typing.Optional['KademliaPeer'] = None if not self.shortlist else self.shortlist[0]
        self.prev_closest_peer: typing.Optional['KademliaPeer'] = None

        self.iteration_queue = asyncio.Queue(loop=self.loop)

        self.running_probes: typing.Set[asyncio.Task] = set()
        self.iteration_count = 0
        self.bottom_out_count = 0
        self.running = False
        self.tasks: typing.List[asyncio.Task] = []
        self.delayed_calls: typing.List[asyncio.Handle] = []
示例#3
0
 def should_split(self, bucket_index: int, to_add: bytes) -> bool:
     #  https://stackoverflow.com/questions/32129978/highly-unbalanced-kademlia-routing-table/32187456#32187456
     if bucket_index < self._split_buckets_under_index:
         return True
     contacts = self.get_peers()
     distance = Distance(self._parent_node_id)
     contacts.sort(key=lambda c: distance(c.node_id))
     kth_contact = contacts[-1] if len(contacts) < constants.k else contacts[constants.k - 1]
     return distance(to_add) < distance(kth_contact.node_id)
示例#4
0
 async def peer_search(self, node_id: bytes, count=constants.k, max_results=constants.k*2,
                       bottom_out_limit=20) -> typing.List['KademliaPeer']:
     accumulated: typing.List['KademliaPeer'] = []
     async with self.peer_search_junction(node_id, max_results=max_results,
                                          bottom_out_limit=bottom_out_limit) as junction:
         async for peers in junction:
             accumulated.extend(peers)
     distance = Distance(node_id)
     accumulated.sort(key=lambda peer: distance(peer.node_id))
     return accumulated[:count]
示例#5
0
 def find_close_peers(self, key: bytes, count: typing.Optional[int] = None,
                      sender_node_id: typing.Optional[bytes] = None) -> typing.List['KademliaPeer']:
     exclude = [self._parent_node_id]
     if sender_node_id:
         exclude.append(sender_node_id)
     count = count or constants.k
     distance = Distance(key)
     contacts = self.get_peers()
     contacts = [c for c in contacts if c.node_id not in exclude]
     if contacts:
         contacts.sort(key=lambda c: distance(c.node_id))
         return contacts[:min(count, len(contacts))]
     return []
示例#6
0
    def get_peers(self,
                  count=-1,
                  exclude_contact=None,
                  sort_distance_to=None) -> typing.List['KademliaPeer']:
        """ Returns a list containing up to the first count number of contacts

        @param count: The amount of contacts to return (if 0 or less, return
                      all contacts)
        @type count: int
        @param exclude_contact: A node node_id to exclude; if this contact is in
                               the list of returned values, it will be
                               discarded before returning. If a C{str} is
                               passed as this argument, it must be the
                               contact's ID.
        @type exclude_contact: str

        @param sort_distance_to: Sort distance to the node_id, defaulting to the parent node node_id. If False don't
                                 sort the contacts

        @raise IndexError: If the number of requested contacts is too large

        @return: Return up to the first count number of contacts in a list
                If no contacts are present an empty is returned
        @rtype: list
        """
        peers = [
            peer for peer in self.peers if peer.node_id != exclude_contact
        ]

        # Return all contacts in bucket
        if count <= 0:
            count = len(peers)

        # Get current contact number
        current_len = len(peers)

        # If count greater than k - return only k contacts
        if count > constants.k:
            count = constants.k

        if not current_len:
            return peers

        if sort_distance_to is False:
            pass
        else:
            sort_distance_to = sort_distance_to or self._node_id
            peers.sort(key=lambda c: Distance(sort_distance_to)(c.node_id))

        return peers[:min(current_len, count)]
示例#7
0
 def __init__(self, peer_manager: 'PeerManager', range_min: int, range_max: int, node_id: bytes):
     """
     @param range_min: The lower boundary for the range in the n-bit ID
                      space covered by this k-bucket
     @param range_max: The upper boundary for the range in the ID space
                      covered by this k-bucket
     """
     self._peer_manager = peer_manager
     self.last_accessed = 0
     self.range_min = range_min
     self.range_max = range_max
     self.peers: typing.List['KademliaPeer'] = []
     self._node_id = node_id
     self._distance_to_self = Distance(node_id)
示例#8
0
def get_shortlist(routing_table: 'TreeRoutingTable', key: bytes,
                  shortlist: typing.Optional[typing.List['KademliaPeer']]) -> typing.List['KademliaPeer']:
    """
    If not provided, initialize the shortlist of peers to probe to the (up to) k closest peers in the routing table

    :param routing_table: a TreeRoutingTable
    :param key: a 48 byte hash
    :param shortlist: optional manually provided shortlist, this is done during bootstrapping when there are no
                      peers in the routing table. During bootstrap the shortlist is set to be the seed nodes.
    """
    if len(key) != constants.hash_length:
        raise ValueError("invalid key length: %i" % len(key))
    if not shortlist:
        shortlist = routing_table.find_close_peers(key)
    distance = Distance(key)
    shortlist.sort(key=lambda peer: distance(peer.node_id), reverse=True)
    return shortlist
示例#9
0
文件: node.py 项目: woolf-wen/lbry
 async def peer_search(
     self,
     node_id: bytes,
     count=constants.k,
     max_results=constants.k * 2,
     bottom_out_limit=20,
     shortlist: typing.Optional[typing.List['KademliaPeer']] = None
 ) -> typing.List['KademliaPeer']:
     peers = []
     async for iteration_peers in self.get_iterative_node_finder(
             node_id,
             shortlist=shortlist,
             bottom_out_limit=bottom_out_limit,
             max_results=max_results):
         peers.extend(iteration_peers)
     distance = Distance(node_id)
     peers.sort(key=lambda peer: distance(peer.node_id))
     return peers[:count]
示例#10
0
 def midpoint_id_in_bucket_range(self, bucket_index: int) -> bytes:
     half = int((self.buckets[bucket_index].range_max - self.buckets[bucket_index].range_min) // 2)
     return Distance(self._parent_node_id)(
         int(self.buckets[bucket_index].range_min + half).to_bytes(constants.hash_length, 'big')
     ).to_bytes(constants.hash_length, 'big')
示例#11
0
 def random_id_in_bucket_range(self, bucket_index: int) -> bytes:
     random_id = int(random.randrange(self.buckets[bucket_index].range_min, self.buckets[bucket_index].range_max))
     return Distance(
         self._parent_node_id
     )(random_id.to_bytes(constants.hash_length, 'big')).to_bytes(constants.hash_length, 'big')