From e7424d367392782fcc0d27b709b263f1c4cebbab Mon Sep 17 00:00:00 2001 From: Alex Haynes Date: Sat, 20 Apr 2019 17:35:05 -0400 Subject: [PATCH] added RoutedHost and updated new_node to support it --- libp2p/__init__.py | 43 +++++++++++++++++++++++++++++---- libp2p/kademlia/kad_peerinfo.py | 3 +-- libp2p/kademlia/network.py | 2 +- libp2p/kademlia/routed_host.py | 35 +++++++++++++++++++++++++++ libp2p/peer/id.py | 3 +++ tests/kademlia/test_basic.py | 14 +++++------ 6 files changed, 85 insertions(+), 15 deletions(-) create mode 100644 libp2p/kademlia/routed_host.py diff --git a/libp2p/__init__.py b/libp2p/__init__.py index fe00408..ae13c3a 100644 --- a/libp2p/__init__.py +++ b/libp2p/__init__.py @@ -6,8 +6,10 @@ from .peer.peerstore import PeerStore from .peer.id import id_from_public_key from .network.swarm import Swarm from .host.basic_host import BasicHost +from .kademlia.routed_host import RoutedHost from .transport.upgrader import TransportUpgrader from .transport.tcp.tcp import TCP +from .kademlia.network import KademliaServer async def cleanup_done_tasks(): @@ -23,6 +25,30 @@ async def cleanup_done_tasks(): # Some sleep necessary to context switch await asyncio.sleep(3) +def generate_id(): + new_key = RSA.generate(2048, e=65537) + new_id = id_from_public_key(new_key.publickey()) + # private_key = new_key.exportKey("PEM") + return new_id + +def initialize_default_kademlia( + ksize=20, alpha=3, id_opt=None, storage=None): + """ + initialize swam when no swarm is passed in + :param ksize: The k parameter from the paper + :param alpha: The alpha parameter from the paper + :param id_opt: optional id for host + :param storage: An instance that implements + :interface:`~kademlia.storage.IStorage` + :return: return a default kademlia instance + """ + if not id_opt: + id_opt = generate_id() + + node_id = id_opt.get_raw_id() + return KademliaServer(ksize=ksize, alpha=alpha, + node_id=node_id, storage=storage) + def initialize_default_swarm( id_opt=None, transport_opt=None, @@ -37,10 +63,9 @@ def initialize_default_swarm( :return: return a default swarm instance """ # pylint: disable=too-many-arguments, unused-argument + if not id_opt: - new_key = RSA.generate(2048, e=65537) - id_opt = id_from_public_key(new_key.publickey()) - # private_key = new_key.exportKey("PEM") + id_opt = generate_id() transport_opt = transport_opt or ["/ip4/127.0.0.1/tcp/8001"] transport = [multiaddr.Multiaddr(t) for t in transport_opt] @@ -58,7 +83,8 @@ def initialize_default_swarm( async def new_node( swarm_opt=None, id_opt=None, transport_opt=None, - muxer_opt=None, sec_opt=None, peerstore_opt=None): + muxer_opt=None, sec_opt=None, peerstore_opt=None, + disc_opt=None): """ create new libp2p node :param id_opt: optional id for host @@ -69,15 +95,22 @@ async def new_node( :return: return a default swarm instance """ # pylint: disable=too-many-arguments + + if not id_opt: + id_opt = generate_id() + if not swarm_opt: swarm_opt = initialize_default_swarm( id_opt=id_opt, transport_opt=transport_opt, muxer_opt=muxer_opt, sec_opt=sec_opt, peerstore_opt=peerstore_opt) + if not disc_opt: + disc_opt = initialize_default_kademlia(ksize=20, alpha=3, + id_opt=id_opt, storage=None) # TODO enable support for other host type # TODO routing unimplemented - host = BasicHost(swarm_opt) + host = RoutedHost(swarm_opt, disc_opt) # Kick off cleanup job asyncio.ensure_future(cleanup_done_tasks()) diff --git a/libp2p/kademlia/kad_peerinfo.py b/libp2p/kademlia/kad_peerinfo.py index cd0bc60..0333fad 100644 --- a/libp2p/kademlia/kad_peerinfo.py +++ b/libp2p/kademlia/kad_peerinfo.py @@ -15,8 +15,7 @@ class KadPeerInfo(PeerInfo): def __init__(self, peer_id, peer_data=None): super(KadPeerInfo, self).__init__(peer_id, peer_data) - # pylint: disable=protected-access - self.peer_id = peer_id._id_str + self.peer_id = peer_id.get_raw_id() self.long_id = int(digest(peer_id._id_str).hex(), 16) self.addrs = peer_data.get_addrs() if peer_data else None diff --git a/libp2p/kademlia/network.py b/libp2p/kademlia/network.py index 30215a0..dbdbef1 100644 --- a/libp2p/kademlia/network.py +++ b/libp2p/kademlia/network.py @@ -16,7 +16,7 @@ log = logging.getLogger(__name__) # pylint: disable=invalid-name # pylint: disable=too-many-instance-attributes -class Server: +class KademliaServer: """ High level view of a node instance. This is the object that should be created to start listening as an active node on the network. diff --git a/libp2p/kademlia/routed_host.py b/libp2p/kademlia/routed_host.py new file mode 100644 index 0000000..9d898fb --- /dev/null +++ b/libp2p/kademlia/routed_host.py @@ -0,0 +1,35 @@ +from libp2p.host.basic_host import BasicHost + +class RoutedHost(BasicHost): + def __init__(self, _network, _kad_network): + super(RoutedHost, self).__init__(_network) + self.kad_network = _kad_network + + def get_kad_network(self): + return self.kad_network + + def kad_listen(self, port, interface='0.0.0.0'): + return self.kad_network.listen(port, interface) + + def kad_get(self, key): + return self.kad_network.get(key) + + def kad_set(self, key, value): + return self.kad_network.set(key, value) + + def kad_set_digest(self, dkey, value): + return self.kad_network.set_digest(dkey, value) + +def check_dht_value_type(value): + """ + Checks to see if the type of the value is a valid type for + placing in the dht. + """ + typeset = [ + int, + float, + bool, + str, + bytes + ] + return type(value) in typeset # pylint: disable=unidiomatic-typecheck diff --git a/libp2p/peer/id.py b/libp2p/peer/id.py index c4d9832..92b4db3 100644 --- a/libp2p/peer/id.py +++ b/libp2p/peer/id.py @@ -15,6 +15,9 @@ class ID: def __init__(self, id_str): self._id_str = id_str + def get_raw_id(self): + return self._id_str + def pretty(self): return base58.b58encode(self._id_str).decode() diff --git a/tests/kademlia/test_basic.py b/tests/kademlia/test_basic.py index bb140b3..b1cc712 100644 --- a/tests/kademlia/test_basic.py +++ b/tests/kademlia/test_basic.py @@ -1,13 +1,13 @@ import pytest -from libp2p.kademlia.network import Server +from libp2p.kademlia.network import KademliaServer @pytest.mark.asyncio async def test_example(): - node_a = Server() + node_a = KademliaServer() await node_a.listen(5678) - node_b = Server() + node_b = KademliaServer() await node_b.listen(5679) # Bootstrap the node by connecting to other known nodes, in this case @@ -29,12 +29,12 @@ async def test_example(): @pytest.mark.asyncio async def test_multiple_nodes_bootstrap_set_get(nodes_nr): - node_bootstrap = Server() + node_bootstrap = KademliaServer() await node_bootstrap.listen(3000 + nodes_nr * 2) nodes = [] for i in range(nodes_nr): - node = Server() + node = KademliaServer() addrs = [("127.0.0.1", 3000 + nodes_nr * 2)] await node.listen(3001 + i + nodes_nr * 2) await node.bootstrap(addrs) @@ -56,12 +56,12 @@ async def test_multiple_nodes_bootstrap_set_get(nodes_nr): @pytest.mark.parametrize("nodes_nr", [(2**i) for i in range(2, 5)]) @pytest.mark.asyncio async def test_multiple_nodes_set_bootstrap_get(nodes_nr): - node_bootstrap = Server() + node_bootstrap = KademliaServer() await node_bootstrap.listen(2000 + nodes_nr * 2) nodes = [] for i in range(nodes_nr): - node = Server() + node = KademliaServer() addrs = [("127.0.0.1", 2000 + nodes_nr * 2)] await node.listen(2001 + i + nodes_nr * 2) await node.bootstrap(addrs)