Added tests for 'RoutedHost' and modified 'FindPeer'

This commit is contained in:
Aratz M. Lasa 2019-10-15 01:01:16 +02:00
parent 3f24b015ab
commit 8143563831
7 changed files with 105 additions and 30 deletions

View File

@ -1,4 +1,5 @@
from libp2p.host.basic_host import BasicHost from libp2p.host.basic_host import BasicHost
from libp2p.host.exceptions import ConnectionFailure
from libp2p.network.network_interface import INetwork from libp2p.network.network_interface import INetwork
from libp2p.peer.peerinfo import PeerInfo from libp2p.peer.peerinfo import PeerInfo
from libp2p.routing.interfaces import IPeerRouting from libp2p.routing.interfaces import IPeerRouting
@ -26,7 +27,10 @@ class RoutedHost(BasicHost):
""" """
# check if we were given some addresses, otherwise, find some with the routing system. # check if we were given some addresses, otherwise, find some with the routing system.
if not peer_info.addrs: if not peer_info.addrs:
peer_info.addrs = (await self._router.find_peer(peer_info.peer_id)).addrs found_peer_info = await self._router.find_peer(peer_info.peer_id)
if not found_peer_info:
raise ConnectionFailure("Unable to find Peer address")
peer_info.addrs = found_peer_info.addrs
self.peerstore.add_addrs(peer_info.peer_id, peer_info.addrs, 10) self.peerstore.add_addrs(peer_info.peer_id, peer_info.addrs, 10)
# there is already a connection to this peer # there is already a connection to this peer

View File

@ -1,3 +1,4 @@
import json
from typing import List, Sequence from typing import List, Sequence
import multiaddr import multiaddr
@ -14,6 +15,17 @@ class PeerInfo:
self.peer_id = peer_id self.peer_id = peer_id
self.addrs = list(addrs) self.addrs = list(addrs)
def to_string(self) -> str:
return json.dumps([self.peer_id.to_string(), list(map(lambda a: str(a), self.addrs))])
def __eq__(self, other):
return isinstance(other, PeerInfo) and self.peer_id == other.peer_id and self.addrs == other.addrs
@classmethod
def info_from_string(cls, info: str) -> "PeerInfo":
peer_id, raw_addrs = json.loads(info)
return PeerInfo(ID.from_base58(peer_id), list(map(lambda a: multiaddr.Multiaddr(a), raw_addrs)))
def info_from_p2p_addr(addr: multiaddr.Multiaddr) -> PeerInfo: def info_from_p2p_addr(addr: multiaddr.Multiaddr) -> PeerInfo:
if not addr: if not addr:

View File

@ -1,42 +1,23 @@
import ast
from typing import Union
from libp2p.kademlia.kad_peerinfo import KadPeerInfo, create_kad_peerinfo
from libp2p.kademlia.network import KademliaServer from libp2p.kademlia.network import KademliaServer
from libp2p.peer.id import ID from libp2p.peer.id import ID
from libp2p.peer.peerinfo import PeerInfo
from libp2p.routing.interfaces import IPeerRouting from libp2p.routing.interfaces import IPeerRouting
class KadmeliaPeerRouter(IPeerRouting): class KadmeliaPeerRouter(IPeerRouting):
server: KademliaServer server: KademliaServer
def __init__(self, dht_server: KademliaServer) -> None: def __init__(self, dht_server: KademliaServer) -> None:
self.server = dht_server self.server = dht_server
async def find_peer(self, peer_id: ID) -> KadPeerInfo: async def find_peer(self, peer_id: ID) -> PeerInfo:
""" """
Find a specific peer Find a specific peer
:param peer_id: peer to search for :param peer_id: peer to search for
:return: KadPeerInfo of specified peer :return: PeerInfo of specified peer
""" """
# switching peer_id to xor_id used by kademlia as node_id # switching peer_id to xor_id used by kademlia as node_id
xor_id = peer_id.xor_id xor_id = peer_id.xor_id
# ignore type for kad # ignore type for kad
value = await self.server.get(xor_id) # type: ignore value = await self.server.get(xor_id) # type: ignore
return decode_peerinfo(value) return PeerInfo.info_from_string(value) if value else None # TODO: should raise error if None?
def decode_peerinfo(encoded: Union[bytes, str]) -> KadPeerInfo:
if isinstance(encoded, bytes):
encoded = encoded.decode()
try:
lines = ast.literal_eval(encoded)
except SyntaxError:
return None
ip = lines[1]
port = lines[2]
peer_id = lines[3]
# ignore typing for kad
peer_info = create_kad_peerinfo(peer_id, ip, port) # type: ignore
return peer_info

0
tests/host/__init__.py Normal file
View File

View File

@ -0,0 +1,53 @@
import asyncio
import pytest
from libp2p.host.exceptions import ConnectionFailure
from libp2p.peer.peerinfo import PeerInfo
from tests.utils import set_up_routers, set_up_nodes_by_transport_opt, set_up_nodes_by_transport_and_disc_opt
@pytest.mark.asyncio
async def test_host_routing_success():
routers = await set_up_routers([5678, 5679])
transports = [["/ip4/127.0.0.1/tcp/0"], ["/ip4/127.0.0.1/tcp/0"]]
transport_disc_opt_list = zip(transports, routers)
(host_a, host_b) = await set_up_nodes_by_transport_and_disc_opt(transport_disc_opt_list)
# Set routing info
await routers[0].server.set(host_a.get_id().xor_id, PeerInfo(host_a.get_id(), host_a.get_addrs()).to_string())
await routers[1].server.set(host_b.get_id().xor_id, PeerInfo(host_b.get_id(), host_b.get_addrs()).to_string())
# forces to use routing as no addrs are provided
await host_a.connect(PeerInfo(host_b.get_id(), []))
await host_b.connect(PeerInfo(host_a.get_id(), []))
# Clean up
await asyncio.gather(*[host_a.close(), host_b.close()])
routers[0].server.stop()
routers[1].server.stop()
@pytest.mark.asyncio
async def test_host_routing_fail():
routers = await set_up_routers([5678, 5679])
transports = [["/ip4/127.0.0.1/tcp/0"], ["/ip4/127.0.0.1/tcp/0"]]
transport_disc_opt_list = zip(transports, routers)
(host_a, host_b) = await set_up_nodes_by_transport_and_disc_opt(transport_disc_opt_list)
host_c = (await set_up_nodes_by_transport_opt([["/ip4/127.0.0.1/tcp/0"]]))[0]
# Set routing info
await routers[0].server.set(host_a.get_id().xor_id, PeerInfo(host_a.get_id(), host_a.get_addrs()).to_string())
await routers[1].server.set(host_b.get_id().xor_id, PeerInfo(host_b.get_id(), host_b.get_addrs()).to_string())
# routing fails because host_c does not use routing
with pytest.raises(ConnectionFailure):
await host_a.connect(PeerInfo(host_c.get_id(), []))
with pytest.raises(ConnectionFailure):
await host_b.connect(PeerInfo(host_c.get_id(), []))
# Clean up
await asyncio.gather(*[host_a.close(), host_b.close(), host_c.close()])
routers[0].server.stop()
routers[1].server.stop()

View File

@ -15,11 +15,11 @@ async def test_simple_two_nodes():
node_a_value = await node_b.bootstrap([("127.0.0.1", 5678)]) node_a_value = await node_b.bootstrap([("127.0.0.1", 5678)])
node_a_kad_peerinfo = node_a_value[0] node_a_kad_peerinfo = node_a_value[0]
await node_a.set(node_a_kad_peerinfo.xor_id, repr(node_a_kad_peerinfo)) await node_a.set(node_a_kad_peerinfo.xor_id, node_a_kad_peerinfo.to_string())
router = KadmeliaPeerRouter(node_b) router = KadmeliaPeerRouter(node_b)
returned_info = await router.find_peer(ID(node_a_kad_peerinfo.peer_id_bytes)) returned_info = await router.find_peer(ID(node_a_kad_peerinfo.peer_id_bytes))
assert repr(returned_info) == repr(node_a_kad_peerinfo) assert returned_info == node_a_kad_peerinfo
@pytest.mark.asyncio @pytest.mark.asyncio
@ -37,11 +37,11 @@ async def test_simple_three_nodes():
node_a_kad_peerinfo = node_a_value[0] node_a_kad_peerinfo = node_a_value[0]
await node_c.bootstrap([("127.0.0.1", 5702)]) await node_c.bootstrap([("127.0.0.1", 5702)])
await node_a.set(node_a_kad_peerinfo.xor_id, repr(node_a_kad_peerinfo)) await node_a.set(node_a_kad_peerinfo.xor_id, node_a_kad_peerinfo.to_string())
router = KadmeliaPeerRouter(node_c) router = KadmeliaPeerRouter(node_c)
returned_info = await router.find_peer(ID(node_a_kad_peerinfo.peer_id_bytes)) returned_info = await router.find_peer(ID(node_a_kad_peerinfo.peer_id_bytes))
assert str(returned_info) == str(node_a_kad_peerinfo) assert returned_info == node_a_kad_peerinfo
@pytest.mark.asyncio @pytest.mark.asyncio
@ -65,8 +65,8 @@ async def test_simple_four_nodes():
await node_d.bootstrap([("127.0.0.1", 5803)]) await node_d.bootstrap([("127.0.0.1", 5803)])
await node_b.set(node_a_kad_peerinfo.xor_id, repr(node_a_kad_peerinfo)) await node_b.set(node_a_kad_peerinfo.xor_id, node_a_kad_peerinfo.to_string())
router = KadmeliaPeerRouter(node_d) router = KadmeliaPeerRouter(node_d)
returned_info = await router.find_peer(ID(node_a_kad_peerinfo.peer_id_bytes)) returned_info = await router.find_peer(ID(node_a_kad_peerinfo.peer_id_bytes))
assert str(returned_info) == str(node_a_kad_peerinfo) assert returned_info == node_a_kad_peerinfo

View File

@ -1,7 +1,9 @@
import multiaddr import multiaddr
from libp2p import new_node from libp2p import new_node
from libp2p.kademlia.network import KademliaServer
from libp2p.peer.peerinfo import info_from_p2p_addr from libp2p.peer.peerinfo import info_from_p2p_addr
from libp2p.routing.kademlia.kademlia_peer_router import KadmeliaPeerRouter
from tests.constants import MAX_READ_LEN from tests.constants import MAX_READ_LEN
@ -36,6 +38,29 @@ async def set_up_nodes_by_transport_opt(transport_opt_list):
return tuple(nodes_list) return tuple(nodes_list)
async def set_up_nodes_by_transport_and_disc_opt(transport_disc_opt_list):
nodes_list = []
for transport_opt, disc_opt in transport_disc_opt_list:
node = await new_node(transport_opt=transport_opt, disc_opt=disc_opt)
await node.get_network().listen(multiaddr.Multiaddr(transport_opt[0]))
nodes_list.append(node)
return tuple(nodes_list)
async def set_up_routers(router_confs):
bootstrap_node = KademliaServer()
await bootstrap_node.listen(router_confs[0])
routers = [KadmeliaPeerRouter(bootstrap_node)]
for port in router_confs[1:]:
node = KademliaServer()
await node.listen(port)
await node.bootstrap_node(("127.0.0.1", router_confs[0]))
routers.append(KadmeliaPeerRouter(node))
return routers
async def echo_stream_handler(stream): async def echo_stream_handler(stream):
while True: while True:
read_string = (await stream.read(MAX_READ_LEN)).decode() read_string = (await stream.read(MAX_READ_LEN)).decode()