forked from LBRYCommunity/lbry-sdk
Merge pull request #1938 from lbryio/fix-kbucket-indexing
fix KBucket.key_in_range
This commit is contained in:
commit
2cf9b2dd9e
2 changed files with 68 additions and 4 deletions
|
@ -29,6 +29,7 @@ class KBucket:
|
||||||
self.range_max = range_max
|
self.range_max = range_max
|
||||||
self.peers: typing.List['KademliaPeer'] = []
|
self.peers: typing.List['KademliaPeer'] = []
|
||||||
self._node_id = node_id
|
self._node_id = node_id
|
||||||
|
self._distance_to_self = Distance(node_id)
|
||||||
|
|
||||||
def add_peer(self, peer: 'KademliaPeer') -> bool:
|
def add_peer(self, peer: 'KademliaPeer') -> bool:
|
||||||
""" Add contact to _contact list in the right order. This will move the
|
""" Add contact to _contact list in the right order. This will move the
|
||||||
|
@ -130,7 +131,7 @@ class KBucket:
|
||||||
if not.
|
if not.
|
||||||
@rtype: bool
|
@rtype: bool
|
||||||
"""
|
"""
|
||||||
return self.range_min <= int.from_bytes(key, 'big') < self.range_max
|
return self.range_min <= self._distance_to_self(key) < self.range_max
|
||||||
|
|
||||||
def __len__(self) -> int:
|
def __len__(self) -> int:
|
||||||
return len(self.peers)
|
return len(self.peers)
|
||||||
|
@ -170,7 +171,7 @@ class TreeRoutingTable:
|
||||||
|
|
||||||
def should_split(self, bucket_index: int, to_add: bytes) -> bool:
|
def should_split(self, bucket_index: int, to_add: bytes) -> bool:
|
||||||
# https://stackoverflow.com/questions/32129978/highly-unbalanced-kademlia-routing-table/32187456#32187456
|
# https://stackoverflow.com/questions/32129978/highly-unbalanced-kademlia-routing-table/32187456#32187456
|
||||||
if self.buckets[bucket_index].key_in_range(self._parent_node_id):
|
if not bucket_index:
|
||||||
return True
|
return True
|
||||||
contacts = self.get_peers()
|
contacts = self.get_peers()
|
||||||
distance = Distance(self._parent_node_id)
|
distance = Distance(self._parent_node_id)
|
||||||
|
@ -236,11 +237,15 @@ class TreeRoutingTable:
|
||||||
|
|
||||||
def random_id_in_bucket_range(self, bucket_index: int) -> bytes:
|
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))
|
random_id = int(random.randrange(self.buckets[bucket_index].range_min, self.buckets[bucket_index].range_max))
|
||||||
return random_id.to_bytes(constants.hash_length, 'big')
|
return Distance(
|
||||||
|
self._parent_node_id
|
||||||
|
)(random_id.to_bytes(constants.hash_length, 'big')).to_bytes(constants.hash_length, 'big')
|
||||||
|
|
||||||
def midpoint_id_in_bucket_range(self, bucket_index: int) -> bytes:
|
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)
|
half = int((self.buckets[bucket_index].range_max - self.buckets[bucket_index].range_min) // 2)
|
||||||
return int(self.buckets[bucket_index].range_min + half).to_bytes(constants.hash_length, 'big')
|
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')
|
||||||
|
|
||||||
def split_bucket(self, old_bucket_index: int) -> None:
|
def split_bucket(self, old_bucket_index: int) -> None:
|
||||||
""" Splits the specified k-bucket into two new buckets which together
|
""" Splits the specified k-bucket into two new buckets which together
|
||||||
|
|
|
@ -6,6 +6,29 @@ from lbrynet.dht.node import Node
|
||||||
from lbrynet.dht.peer import PeerManager
|
from lbrynet.dht.peer import PeerManager
|
||||||
|
|
||||||
|
|
||||||
|
expected_ranges = [
|
||||||
|
(
|
||||||
|
0,
|
||||||
|
2462625387274654950767440006258975862817483704404090416746768337765357610718575663213391640930307227550414249394176
|
||||||
|
),
|
||||||
|
(
|
||||||
|
2462625387274654950767440006258975862817483704404090416746768337765357610718575663213391640930307227550414249394176,
|
||||||
|
4925250774549309901534880012517951725634967408808180833493536675530715221437151326426783281860614455100828498788352
|
||||||
|
),
|
||||||
|
(
|
||||||
|
4925250774549309901534880012517951725634967408808180833493536675530715221437151326426783281860614455100828498788352,
|
||||||
|
9850501549098619803069760025035903451269934817616361666987073351061430442874302652853566563721228910201656997576704
|
||||||
|
),
|
||||||
|
(
|
||||||
|
9850501549098619803069760025035903451269934817616361666987073351061430442874302652853566563721228910201656997576704,
|
||||||
|
19701003098197239606139520050071806902539869635232723333974146702122860885748605305707133127442457820403313995153408
|
||||||
|
),
|
||||||
|
(
|
||||||
|
19701003098197239606139520050071806902539869635232723333974146702122860885748605305707133127442457820403313995153408,
|
||||||
|
39402006196394479212279040100143613805079739270465446667948293404245721771497210611414266254884915640806627990306816
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
class TestRouting(AsyncioTestCase):
|
class TestRouting(AsyncioTestCase):
|
||||||
async def test_fill_one_bucket(self):
|
async def test_fill_one_bucket(self):
|
||||||
loop = asyncio.get_event_loop()
|
loop = asyncio.get_event_loop()
|
||||||
|
@ -43,6 +66,42 @@ class TestRouting(AsyncioTestCase):
|
||||||
for node in nodes.values():
|
for node in nodes.values():
|
||||||
node.protocol.stop()
|
node.protocol.stop()
|
||||||
|
|
||||||
|
async def test_split_buckets(self):
|
||||||
|
loop = asyncio.get_event_loop()
|
||||||
|
peer_addresses = [
|
||||||
|
(constants.generate_id(1), '1.2.3.1'),
|
||||||
|
]
|
||||||
|
for i in range(2, 200):
|
||||||
|
peer_addresses.append((constants.generate_id(i), f'1.2.3.{i}'))
|
||||||
|
with dht_mocks.mock_network_loop(loop):
|
||||||
|
nodes = {
|
||||||
|
i: Node(loop, PeerManager(loop), node_id, 4444, 4444, 3333, address)
|
||||||
|
for i, (node_id, address) in enumerate(peer_addresses)
|
||||||
|
}
|
||||||
|
node_1 = nodes[0]
|
||||||
|
for i in range(1, len(peer_addresses)):
|
||||||
|
node = nodes[i]
|
||||||
|
peer = node_1.protocol.peer_manager.get_kademlia_peer(
|
||||||
|
node.protocol.node_id, node.protocol.external_ip,
|
||||||
|
udp_port=node.protocol.udp_port
|
||||||
|
)
|
||||||
|
# set all of the peers to good (as to not attempt pinging stale ones during split)
|
||||||
|
node_1.protocol.peer_manager.report_last_replied(peer.address, peer.udp_port)
|
||||||
|
node_1.protocol.peer_manager.report_last_replied(peer.address, peer.udp_port)
|
||||||
|
await node_1.protocol.add_peer(peer)
|
||||||
|
# check that bucket 0 is always the one covering the local node id
|
||||||
|
self.assertEqual(True, node_1.protocol.routing_table.buckets[0].key_in_range(node_1.protocol.node_id))
|
||||||
|
self.assertEqual(40, len(node_1.protocol.routing_table.get_peers()))
|
||||||
|
self.assertEqual(len(expected_ranges), len(node_1.protocol.routing_table.buckets))
|
||||||
|
covered = 0
|
||||||
|
for (expected_min, expected_max), bucket in zip(expected_ranges, node_1.protocol.routing_table.buckets):
|
||||||
|
self.assertEqual(expected_min, bucket.range_min)
|
||||||
|
self.assertEqual(expected_max, bucket.range_max)
|
||||||
|
covered += bucket.range_max - bucket.range_min
|
||||||
|
self.assertEqual(2**384, covered)
|
||||||
|
for node in nodes.values():
|
||||||
|
node.stop()
|
||||||
|
|
||||||
|
|
||||||
# from binascii import hexlify, unhexlify
|
# from binascii import hexlify, unhexlify
|
||||||
#
|
#
|
||||||
|
|
Loading…
Reference in a new issue