2019-05-07 11:44:13 +08:00
|
|
|
import asyncio
|
|
|
|
import multiaddr
|
2019-04-02 04:55:44 +08:00
|
|
|
import uuid
|
2019-05-07 11:44:13 +08:00
|
|
|
import random
|
2019-04-03 10:05:32 +08:00
|
|
|
import struct
|
2019-07-25 14:08:16 +08:00
|
|
|
from typing import (
|
|
|
|
Sequence,
|
|
|
|
)
|
|
|
|
|
2019-05-07 11:44:13 +08:00
|
|
|
from libp2p import new_node
|
2019-04-02 04:55:44 +08:00
|
|
|
from libp2p.pubsub.pb import rpc_pb2
|
2019-05-07 11:44:13 +08:00
|
|
|
from libp2p.peer.peerinfo import info_from_p2p_addr
|
2019-07-25 14:08:16 +08:00
|
|
|
from libp2p.peer.id import ID
|
2019-05-07 11:44:13 +08:00
|
|
|
from libp2p.pubsub.pubsub import Pubsub
|
|
|
|
from libp2p.pubsub.gossipsub import GossipSub
|
2019-04-02 04:55:44 +08:00
|
|
|
|
2019-04-03 10:05:32 +08:00
|
|
|
|
|
|
|
def message_id_generator(start_val):
|
2019-04-02 04:55:44 +08:00
|
|
|
"""
|
|
|
|
Generate a unique message id
|
2019-04-03 10:05:32 +08:00
|
|
|
:param start_val: value to start generating messages at
|
|
|
|
:return: message id
|
2019-04-02 04:55:44 +08:00
|
|
|
"""
|
2019-04-03 10:05:32 +08:00
|
|
|
val = start_val
|
|
|
|
def generator():
|
|
|
|
# Allow manipulation of val within closure
|
|
|
|
nonlocal val
|
|
|
|
|
|
|
|
# Increment id
|
|
|
|
val += 1
|
|
|
|
|
|
|
|
# Convert val to big endian
|
2019-04-06 05:30:35 +08:00
|
|
|
return struct.pack('>Q', val)
|
2019-04-03 10:05:32 +08:00
|
|
|
|
|
|
|
return generator
|
2019-04-02 04:55:44 +08:00
|
|
|
|
2019-07-25 14:08:16 +08:00
|
|
|
|
|
|
|
def make_pubsub_msg(
|
|
|
|
origin_id: ID,
|
|
|
|
topic_ids: Sequence[str],
|
|
|
|
data: bytes,
|
|
|
|
seqno: bytes) -> rpc_pb2.Message:
|
|
|
|
return rpc_pb2.Message(
|
|
|
|
from_id=origin_id.to_bytes(),
|
|
|
|
seqno=seqno,
|
|
|
|
data=data,
|
|
|
|
topicIDs=list(topic_ids),
|
|
|
|
)
|
|
|
|
|
|
|
|
|
2019-04-02 04:55:44 +08:00
|
|
|
def generate_RPC_packet(origin_id, topics, msg_content, msg_id):
|
|
|
|
"""
|
|
|
|
Generate RPC packet to send over wire
|
|
|
|
:param origin_id: peer id of the message origin
|
|
|
|
:param topics: list of topics
|
|
|
|
:param msg_content: string of content in data
|
|
|
|
:param msg_id: seqno for the message
|
|
|
|
"""
|
|
|
|
packet = rpc_pb2.RPC()
|
|
|
|
message = rpc_pb2.Message(
|
|
|
|
from_id=origin_id.encode('utf-8'),
|
2019-04-03 10:05:32 +08:00
|
|
|
seqno=msg_id,
|
2019-04-02 04:55:44 +08:00
|
|
|
data=msg_content.encode('utf-8'),
|
2019-07-25 14:08:16 +08:00
|
|
|
)
|
2019-04-02 04:55:44 +08:00
|
|
|
|
|
|
|
for topic in topics:
|
|
|
|
message.topicIDs.extend([topic.encode('utf-8')])
|
|
|
|
|
|
|
|
packet.publish.extend([message])
|
|
|
|
return packet
|
2019-05-07 11:44:13 +08:00
|
|
|
|
2019-07-25 14:08:16 +08:00
|
|
|
|
2019-05-07 11:44:13 +08:00
|
|
|
async def connect(node1, node2):
|
|
|
|
"""
|
|
|
|
Connect node1 to node2
|
|
|
|
"""
|
|
|
|
addr = node2.get_addrs()[0]
|
|
|
|
info = info_from_p2p_addr(addr)
|
|
|
|
await node1.connect(info)
|
|
|
|
|
|
|
|
async def create_libp2p_hosts(num_hosts):
|
|
|
|
"""
|
|
|
|
Create libp2p hosts
|
|
|
|
:param num_hosts: number of hosts to create
|
|
|
|
"""
|
|
|
|
hosts = []
|
|
|
|
tasks_create = []
|
|
|
|
for i in range(0, num_hosts):
|
|
|
|
# Create node
|
|
|
|
tasks_create.append(asyncio.ensure_future(new_node(transport_opt=["/ip4/127.0.0.1/tcp/0"])))
|
|
|
|
hosts = await asyncio.gather(*tasks_create)
|
|
|
|
|
|
|
|
tasks_listen = []
|
|
|
|
for node in hosts:
|
|
|
|
# Start listener
|
|
|
|
tasks_listen.append(asyncio.ensure_future(node.get_network().listen(multiaddr.Multiaddr("/ip4/127.0.0.1/tcp/0"))))
|
|
|
|
await asyncio.gather(*tasks_listen)
|
|
|
|
|
|
|
|
return hosts
|
|
|
|
|
|
|
|
def create_pubsub_and_gossipsub_instances(libp2p_hosts, supported_protocols, degree, degree_low, \
|
|
|
|
degree_high, time_to_live, gossip_window, gossip_history, heartbeat_interval):
|
|
|
|
pubsubs = []
|
|
|
|
gossipsubs = []
|
|
|
|
for node in libp2p_hosts:
|
|
|
|
gossipsub = GossipSub(supported_protocols, degree,
|
|
|
|
degree_low, degree_high, time_to_live,
|
|
|
|
gossip_window, gossip_history,
|
|
|
|
heartbeat_interval)
|
|
|
|
pubsub = Pubsub(node, gossipsub, "a")
|
|
|
|
pubsubs.append(pubsub)
|
|
|
|
gossipsubs.append(gossipsub)
|
|
|
|
|
|
|
|
return pubsubs, gossipsubs
|
|
|
|
|
|
|
|
async def sparse_connect(hosts):
|
|
|
|
await connect_some(hosts, 3)
|
|
|
|
|
|
|
|
|
|
|
|
async def dense_connect(hosts):
|
|
|
|
await connect_some(hosts, 10)
|
|
|
|
|
|
|
|
|
|
|
|
async def connect_some(hosts, degree):
|
|
|
|
for i, host in enumerate(hosts):
|
|
|
|
for j, host2 in enumerate(hosts):
|
|
|
|
if i != j and i < j:
|
|
|
|
await connect(host, host2)
|
|
|
|
|
|
|
|
# TODO: USE THE CODE BELOW
|
|
|
|
# for i, host in enumerate(hosts):
|
|
|
|
# j = 0
|
|
|
|
# while j < degree:
|
|
|
|
# n = random.randint(0, len(hosts) - 1)
|
|
|
|
|
|
|
|
# if n == i:
|
|
|
|
# j -= 1
|
|
|
|
# continue
|
|
|
|
|
|
|
|
# neighbor = hosts[n]
|
|
|
|
|
|
|
|
# await connect(host, neighbor)
|
|
|
|
|
|
|
|
# j += 1
|
2019-07-18 19:39:57 +08:00
|
|
|
|
|
|
|
async def one_to_all_connect(hosts, central_host_index):
|
|
|
|
for i, host in enumerate(hosts):
|
|
|
|
if i != central_host_index:
|
|
|
|
await connect(hosts[central_host_index], host)
|