mirror of
https://github.com/varun-r-mallya/py-libp2p.git
synced 2025-12-31 20:36:24 +00:00
Merge branch 'master' into feature/porting-to-trio
This commit is contained in:
0
tests/core/conftest.py
Normal file
0
tests/core/conftest.py
Normal file
2
tests/core/test_import.py
Normal file
2
tests/core/test_import.py
Normal file
@ -0,0 +1,2 @@
|
||||
def test_import():
|
||||
import libp2p # noqa: F401
|
||||
@ -7,7 +7,7 @@ from libp2p.host.defaults import get_default_protocols
|
||||
def test_default_protocols():
|
||||
key_pair = create_new_key_pair()
|
||||
swarm = initialize_default_swarm(key_pair)
|
||||
host = BasicHost(key_pair.public_key, swarm)
|
||||
host = BasicHost(swarm)
|
||||
|
||||
mux = host.get_mux()
|
||||
handlers = mux.handlers
|
||||
|
||||
26
tests/host/test_routed_host.py
Normal file
26
tests/host/test_routed_host.py
Normal file
@ -0,0 +1,26 @@
|
||||
import pytest
|
||||
|
||||
from libp2p.host.exceptions import ConnectionFailure
|
||||
from libp2p.peer.peerinfo import PeerInfo
|
||||
from libp2p.tools.factories import HostFactory, RoutedHostFactory
|
||||
|
||||
|
||||
@pytest.mark.trio
|
||||
async def test_host_routing_success():
|
||||
async with RoutedHostFactory.create_batch_and_listen(False, 2) as hosts:
|
||||
# forces to use routing as no addrs are provided
|
||||
await hosts[0].connect(PeerInfo(hosts[1].get_id(), []))
|
||||
await hosts[1].connect(PeerInfo(hosts[0].get_id(), []))
|
||||
|
||||
|
||||
@pytest.mark.trio
|
||||
async def test_host_routing_fail():
|
||||
is_secure = False
|
||||
async with RoutedHostFactory.create_batch_and_listen(
|
||||
is_secure, 2
|
||||
) as routed_hosts, HostFactory.create_batch_and_listen(is_secure, 1) as basic_hosts:
|
||||
# routing fails because host_c does not use routing
|
||||
with pytest.raises(ConnectionFailure):
|
||||
await routed_hosts[0].connect(PeerInfo(basic_hosts[0].get_id(), []))
|
||||
with pytest.raises(ConnectionFailure):
|
||||
await routed_hosts[1].connect(PeerInfo(basic_hosts[0].get_id(), []))
|
||||
@ -1,79 +0,0 @@
|
||||
import pytest
|
||||
|
||||
from libp2p.kademlia.network import KademliaServer
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_example():
|
||||
node_a = KademliaServer()
|
||||
await node_a.listen()
|
||||
|
||||
node_b = KademliaServer()
|
||||
await node_b.listen()
|
||||
|
||||
# Bootstrap the node by connecting to other known nodes, in this case
|
||||
# replace 123.123.123.123 with the IP of another node and optionally
|
||||
# give as many ip/port combos as you can for other nodes.
|
||||
await node_b.bootstrap([node_a.address])
|
||||
|
||||
# set a value for the key "my-key" on the network
|
||||
value = "my-value"
|
||||
key = "my-key"
|
||||
await node_b.set(key, value)
|
||||
|
||||
# get the value associated with "my-key" from the network
|
||||
assert await node_b.get(key) == value
|
||||
assert await node_a.get(key) == value
|
||||
|
||||
|
||||
@pytest.mark.parametrize("nodes_nr", [(2 ** i) for i in range(2, 5)])
|
||||
@pytest.mark.asyncio
|
||||
async def test_multiple_nodes_bootstrap_set_get(nodes_nr):
|
||||
|
||||
node_bootstrap = KademliaServer()
|
||||
await node_bootstrap.listen(3000 + nodes_nr * 2)
|
||||
|
||||
nodes = []
|
||||
for i in range(nodes_nr):
|
||||
node = KademliaServer()
|
||||
addrs = [("127.0.0.1", 3000 + nodes_nr * 2)]
|
||||
await node.listen(3001 + i + nodes_nr * 2)
|
||||
await node.bootstrap(addrs)
|
||||
nodes.append(node)
|
||||
|
||||
for i, node in enumerate(nodes):
|
||||
# set a value for the key "my-key" on the network
|
||||
value = "my awesome value %d" % i
|
||||
key = "set from %d" % i
|
||||
await node.set(key, value)
|
||||
|
||||
for i in range(nodes_nr):
|
||||
for node in nodes:
|
||||
value = "my awesome value %d" % i
|
||||
key = "set from %d" % i
|
||||
assert await node.get(key) == value
|
||||
|
||||
|
||||
@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 = KademliaServer()
|
||||
await node_bootstrap.listen(2000 + nodes_nr * 2)
|
||||
|
||||
nodes = []
|
||||
for i in range(nodes_nr):
|
||||
node = KademliaServer()
|
||||
addrs = [("127.0.0.1", 2000 + nodes_nr * 2)]
|
||||
await node.listen(2001 + i + nodes_nr * 2)
|
||||
await node.bootstrap(addrs)
|
||||
|
||||
value = "my awesome value %d" % i
|
||||
key = "set from %d" % i
|
||||
await node.set(key, value)
|
||||
nodes.append(node)
|
||||
|
||||
for i in range(nodes_nr):
|
||||
for node in nodes:
|
||||
value = "my awesome value %d" % i
|
||||
key = "set from %d" % i
|
||||
assert await node.get(key) == value
|
||||
@ -1,30 +0,0 @@
|
||||
import pytest
|
||||
|
||||
from libp2p.kademlia.network import KademliaServer
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_example():
|
||||
node_a = KademliaServer()
|
||||
await node_a.listen()
|
||||
|
||||
node_b = KademliaServer()
|
||||
await node_b.listen()
|
||||
await node_b.bootstrap([node_a.address])
|
||||
|
||||
key = "hello"
|
||||
value = "world"
|
||||
await node_b.set(key, value)
|
||||
await node_b.provide("hello")
|
||||
|
||||
providers = await node_b.get_providers("hello")
|
||||
|
||||
# bmuller's handle_call_response wraps
|
||||
# every rpc call result in a list of tuples
|
||||
# [(True, [b'\xf9\xa1\xf5\x10a\xe5\xe0F'])]
|
||||
first_tuple = providers[0]
|
||||
# (True, [b'\xf9\xa1\xf5\x10a\xe5\xe0F'])
|
||||
first_providers = first_tuple[1]
|
||||
# [b'\xf9\xa1\xf5\x10a\xe5\xe0F']
|
||||
first_provider = first_providers[0]
|
||||
assert node_b.node.peer_id_bytes == first_provider
|
||||
@ -1,10 +1,9 @@
|
||||
import multiaddr
|
||||
import pytest
|
||||
|
||||
from libp2p.peer.peerinfo import info_from_p2p_addr
|
||||
from libp2p.tools.constants import MAX_READ_LEN
|
||||
from libp2p.tools.factories import HostFactory
|
||||
from libp2p.tools.utils import create_echo_stream_handler
|
||||
from libp2p.tools.utils import connect, create_echo_stream_handler
|
||||
from libp2p.typing import TProtocol
|
||||
|
||||
PROTOCOL_ID_0 = TProtocol("/echo/0")
|
||||
@ -261,18 +260,14 @@ async def test_triangle_nodes_connection(is_host_secure):
|
||||
@pytest.mark.trio
|
||||
async def test_host_connect(is_host_secure):
|
||||
async with HostFactory.create_batch_and_listen(is_host_secure, 2) as hosts:
|
||||
assert not hosts[0].get_peerstore().peer_ids()
|
||||
|
||||
addr = hosts[1].get_addrs()[0]
|
||||
info = info_from_p2p_addr(addr)
|
||||
await hosts[0].connect(info)
|
||||
|
||||
assert len(hosts[0].get_peerstore().peer_ids()) == 1
|
||||
|
||||
await hosts[0].connect(info)
|
||||
await connect(hosts[0], hosts[1])
|
||||
assert len(hosts[0].get_peerstore().peer_ids()) == 2
|
||||
|
||||
await connect(hosts[0], hosts[1])
|
||||
# make sure we don't do double connection
|
||||
assert len(hosts[0].get_peerstore().peer_ids()) == 1
|
||||
assert len(hosts[0].get_peerstore().peer_ids()) == 2
|
||||
|
||||
assert hosts[1].get_id() in hosts[0].get_peerstore().peer_ids()
|
||||
ma_node_b = multiaddr.Multiaddr("/p2p/%s" % hosts[1].get_id().pretty())
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
from multiaddr import Multiaddr
|
||||
import pytest
|
||||
import trio
|
||||
from trio.testing import wait_all_tasks_blocked
|
||||
@ -86,3 +87,56 @@ async def test_swarm_remove_conn(swarm_pair):
|
||||
# Test: Remove twice. There should not be errors.
|
||||
swarm_0.remove_conn(conn_0)
|
||||
assert swarm_1.get_peer_id() not in swarm_0.connections
|
||||
|
||||
|
||||
@pytest.mark.trio
|
||||
async def test_swarm_multiaddr(is_host_secure):
|
||||
async with SwarmFactory.create_batch_and_listen(is_host_secure, 3) as swarms:
|
||||
|
||||
def clear():
|
||||
swarms[0].peerstore.clear_addrs(swarms[1].get_peer_id())
|
||||
|
||||
clear()
|
||||
# No addresses
|
||||
with pytest.raises(SwarmException):
|
||||
await swarms[0].dial_peer(swarms[1].get_peer_id())
|
||||
|
||||
clear()
|
||||
# Wrong addresses
|
||||
swarms[0].peerstore.add_addrs(
|
||||
swarms[1].get_peer_id(), [Multiaddr("/ip4/0.0.0.0/tcp/9999")], 10000
|
||||
)
|
||||
|
||||
with pytest.raises(SwarmException):
|
||||
await swarms[0].dial_peer(swarms[1].get_peer_id())
|
||||
|
||||
clear()
|
||||
# Multiple wrong addresses
|
||||
swarms[0].peerstore.add_addrs(
|
||||
swarms[1].get_peer_id(),
|
||||
[Multiaddr("/ip4/0.0.0.0/tcp/9999"), Multiaddr("/ip4/0.0.0.0/tcp/9998")],
|
||||
10000,
|
||||
)
|
||||
|
||||
with pytest.raises(SwarmException):
|
||||
await swarms[0].dial_peer(swarms[1].get_peer_id())
|
||||
|
||||
# Test one address
|
||||
addrs = tuple(
|
||||
addr
|
||||
for transport in swarms[1].listeners.values()
|
||||
for addr in transport.get_addrs()
|
||||
)
|
||||
|
||||
swarms[0].peerstore.add_addrs(swarms[1].get_peer_id(), addrs[:1], 10000)
|
||||
await swarms[0].dial_peer(swarms[1].get_peer_id())
|
||||
|
||||
# Test multiple addresses
|
||||
addrs = tuple(
|
||||
addr
|
||||
for transport in swarms[1].listeners.values()
|
||||
for addr in transport.get_addrs()
|
||||
)
|
||||
|
||||
swarms[0].peerstore.add_addrs(swarms[1].get_peer_id(), addrs + addrs, 10000)
|
||||
await swarms[0].dial_peer(swarms[1].get_peer_id())
|
||||
|
||||
@ -1,13 +1,14 @@
|
||||
from libp2p.peer.peerstore import PeerStore
|
||||
import pytest
|
||||
|
||||
from libp2p.peer.peerstore import PeerStore, PeerStoreError
|
||||
|
||||
# Testing methods from IPeerStore base class.
|
||||
|
||||
|
||||
def test_peer_info_empty():
|
||||
store = PeerStore()
|
||||
info = store.peer_info("peer")
|
||||
|
||||
assert not info
|
||||
with pytest.raises(PeerStoreError):
|
||||
store.peer_info("peer")
|
||||
|
||||
|
||||
def test_peer_info_basic():
|
||||
|
||||
58
tests/pubsub/conftest.py
Normal file
58
tests/pubsub/conftest.py
Normal file
@ -0,0 +1,58 @@
|
||||
import pytest
|
||||
|
||||
from libp2p.tools.constants import GOSSIPSUB_PARAMS
|
||||
from libp2p.tools.factories import FloodsubFactory, GossipsubFactory, PubsubFactory
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def is_strict_signing():
|
||||
return False
|
||||
|
||||
|
||||
def _make_pubsubs(hosts, pubsub_routers, cache_size, is_strict_signing):
|
||||
if len(pubsub_routers) != len(hosts):
|
||||
raise ValueError(
|
||||
f"lenght of pubsub_routers={pubsub_routers} should be equaled to the "
|
||||
f"length of hosts={len(hosts)}"
|
||||
)
|
||||
return tuple(
|
||||
PubsubFactory(
|
||||
host=host,
|
||||
router=router,
|
||||
cache_size=cache_size,
|
||||
strict_signing=is_strict_signing,
|
||||
)
|
||||
for host, router in zip(hosts, pubsub_routers)
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def pubsub_cache_size():
|
||||
return None # default
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def gossipsub_params():
|
||||
return GOSSIPSUB_PARAMS
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def pubsubs_fsub(num_hosts, hosts, pubsub_cache_size, is_strict_signing):
|
||||
floodsubs = FloodsubFactory.create_batch(num_hosts)
|
||||
_pubsubs_fsub = _make_pubsubs(
|
||||
hosts, floodsubs, pubsub_cache_size, is_strict_signing
|
||||
)
|
||||
yield _pubsubs_fsub
|
||||
# TODO: Clean up
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def pubsubs_gsub(
|
||||
num_hosts, hosts, pubsub_cache_size, gossipsub_params, is_strict_signing
|
||||
):
|
||||
gossipsubs = GossipsubFactory.create_batch(num_hosts, **gossipsub_params._asdict())
|
||||
_pubsubs_gsub = _make_pubsubs(
|
||||
hosts, gossipsubs, pubsub_cache_size, is_strict_signing
|
||||
)
|
||||
yield _pubsubs_gsub
|
||||
# TODO: Clean up
|
||||
@ -3,7 +3,8 @@ import random
|
||||
import pytest
|
||||
import trio
|
||||
|
||||
from libp2p.tools.factories import PubsubFactory
|
||||
from libp2p.pubsub.gossipsub import PROTOCOL_ID
|
||||
from libp2p.tools.factories import IDFactory, PubsubFactory
|
||||
from libp2p.tools.pubsub.utils import dense_connect, one_to_all_connect
|
||||
from libp2p.tools.utils import connect
|
||||
|
||||
@ -109,7 +110,7 @@ async def test_handle_graft(monkeypatch):
|
||||
monkeypatch.setattr(gossipsubs[index_bob], "emit_prune", emit_prune)
|
||||
|
||||
# Check that alice is bob's peer but not his mesh peer
|
||||
assert id_alice in gossipsubs[index_bob].peers_gossipsub
|
||||
assert gossipsubs[index_bob].peer_protocol[id_alice] == PROTOCOL_ID
|
||||
assert topic not in gossipsubs[index_bob].mesh
|
||||
|
||||
await gossipsubs[index_alice].emit_graft(topic, id_bob)
|
||||
@ -120,7 +121,7 @@ async def test_handle_graft(monkeypatch):
|
||||
# Check that bob is alice's peer but not her mesh peer
|
||||
assert topic in gossipsubs[index_alice].mesh
|
||||
assert id_bob not in gossipsubs[index_alice].mesh[topic]
|
||||
assert id_bob in gossipsubs[index_alice].peers_gossipsub
|
||||
assert gossipsubs[index_alice].peer_protocol[id_bob] == PROTOCOL_ID
|
||||
|
||||
await gossipsubs[index_bob].emit_graft(topic, id_alice)
|
||||
|
||||
@ -148,8 +149,8 @@ async def test_handle_prune():
|
||||
|
||||
await connect(pubsubs_gsub[index_alice].host, pubsubs_gsub[index_bob].host)
|
||||
|
||||
# Wait 3 seconds for heartbeat to allow mesh to connect
|
||||
await trio.sleep(3)
|
||||
# Wait for heartbeat to allow mesh to connect
|
||||
await trio.sleep(1)
|
||||
|
||||
# Check that they are each other's mesh peer
|
||||
assert id_alice in gossipsubs[index_bob].mesh[topic]
|
||||
@ -158,15 +159,16 @@ async def test_handle_prune():
|
||||
# alice emit prune message to bob, alice should be removed
|
||||
# from bob's mesh peer
|
||||
await gossipsubs[index_alice].emit_prune(topic, id_bob)
|
||||
# `emit_prune` does not remove bob from alice's mesh peers
|
||||
assert id_bob in gossipsubs[index_alice].mesh[topic]
|
||||
|
||||
# FIXME: This test currently works because the heartbeat interval
|
||||
# is increased to 3 seconds, so alice won't get add back into
|
||||
# bob's mesh peer during heartbeat.
|
||||
await trio.sleep(1)
|
||||
# NOTE: We increase `heartbeat_interval` to 3 seconds so that bob will not
|
||||
# add alice back to his mesh after heartbeat.
|
||||
# Wait for bob to `handle_prune`
|
||||
await trio.sleep(0.1)
|
||||
|
||||
# Check that alice is no longer bob's mesh peer
|
||||
assert id_alice not in gossipsubs[index_bob].mesh[topic]
|
||||
assert id_bob in gossipsubs[index_alice].mesh[topic]
|
||||
|
||||
|
||||
@pytest.mark.trio
|
||||
@ -329,7 +331,7 @@ async def test_gossip_propagation():
|
||||
2, degree=1, degree_low=0, degree_high=2, gossip_window=50, gossip_history=100
|
||||
) as pubsubs_gsub:
|
||||
topic = "foo"
|
||||
await pubsubs_gsub[0].subscribe(topic)
|
||||
queue_0 = await pubsubs_gsub[0].subscribe(topic)
|
||||
|
||||
# node 0 publish to topic
|
||||
msg_content = b"foo_msg"
|
||||
@ -337,14 +339,139 @@ async def test_gossip_propagation():
|
||||
# publish from the randomly chosen host
|
||||
await pubsubs_gsub[0].publish(topic, msg_content)
|
||||
|
||||
# now node 1 subscribes
|
||||
queue_1 = await pubsubs_gsub[1].subscribe(topic)
|
||||
|
||||
await connect(pubsubs_gsub[0].host, pubsubs_gsub[1].host)
|
||||
|
||||
# wait for gossip heartbeat
|
||||
await trio.sleep(2)
|
||||
|
||||
# should be able to read message
|
||||
msg = await queue_1.get()
|
||||
await trio.sleep(0.5)
|
||||
# Assert that the blocking queues receive the message
|
||||
msg = await queue_0.get()
|
||||
assert msg.data == msg_content
|
||||
|
||||
|
||||
@pytest.mark.parametrize("initial_mesh_peer_count", (7, 10, 13))
|
||||
@pytest.mark.trio
|
||||
async def test_mesh_heartbeat(initial_mesh_peer_count, monkeypatch):
|
||||
async with PubsubFactory.create_batch_with_gossipsub(
|
||||
1, heartbeat_initial_delay=100
|
||||
) as pubsubs_gsub:
|
||||
# It's difficult to set up the initial peer subscription condition.
|
||||
# Ideally I would like to have initial mesh peer count that's below ``GossipSubDegree``
|
||||
# so I can test if `mesh_heartbeat` return correct peers to GRAFT.
|
||||
# The problem is that I can not set it up so that we have peers subscribe to the topic
|
||||
# but not being part of our mesh peers (as these peers are the peers to GRAFT).
|
||||
# So I monkeypatch the peer subscriptions and our mesh peers.
|
||||
total_peer_count = 14
|
||||
topic = "TEST_MESH_HEARTBEAT"
|
||||
|
||||
fake_peer_ids = [IDFactory() for _ in range(total_peer_count)]
|
||||
peer_protocol = {peer_id: PROTOCOL_ID for peer_id in fake_peer_ids}
|
||||
monkeypatch.setattr(pubsubs_gsub[0].router, "peer_protocol", peer_protocol)
|
||||
|
||||
peer_topics = {topic: set(fake_peer_ids)}
|
||||
# Monkeypatch the peer subscriptions
|
||||
monkeypatch.setattr(pubsubs_gsub[0], "peer_topics", peer_topics)
|
||||
|
||||
mesh_peer_indices = random.sample(
|
||||
range(total_peer_count), initial_mesh_peer_count
|
||||
)
|
||||
mesh_peers = [fake_peer_ids[i] for i in mesh_peer_indices]
|
||||
router_mesh = {topic: set(mesh_peers)}
|
||||
# Monkeypatch our mesh peers
|
||||
monkeypatch.setattr(pubsubs_gsub[0].router, "mesh", router_mesh)
|
||||
|
||||
peers_to_graft, peers_to_prune = pubsubs_gsub[0].router.mesh_heartbeat()
|
||||
if initial_mesh_peer_count > pubsubs_gsub[0].router.degree:
|
||||
# If number of initial mesh peers is more than `GossipSubDegree`,
|
||||
# we should PRUNE mesh peers
|
||||
assert len(peers_to_graft) == 0
|
||||
assert (
|
||||
len(peers_to_prune)
|
||||
== initial_mesh_peer_count - pubsubs_gsub[0].router.degree
|
||||
)
|
||||
for peer in peers_to_prune:
|
||||
assert peer in mesh_peers
|
||||
elif initial_mesh_peer_count < pubsubs_gsub[0].router.degree:
|
||||
# If number of initial mesh peers is less than `GossipSubDegree`,
|
||||
# we should GRAFT more peers
|
||||
assert len(peers_to_prune) == 0
|
||||
assert (
|
||||
len(peers_to_graft)
|
||||
== pubsubs_gsub[0].router.degree - initial_mesh_peer_count
|
||||
)
|
||||
for peer in peers_to_graft:
|
||||
assert peer not in mesh_peers
|
||||
else:
|
||||
assert len(peers_to_prune) == 0 and len(peers_to_graft) == 0
|
||||
|
||||
|
||||
@pytest.mark.parametrize("initial_peer_count", (1, 4, 7))
|
||||
@pytest.mark.trio
|
||||
async def test_gossip_heartbeat(initial_peer_count, monkeypatch):
|
||||
async with PubsubFactory.create_batch_with_gossipsub(
|
||||
1, heartbeat_initial_delay=100
|
||||
) as pubsubs_gsub:
|
||||
# The problem is that I can not set it up so that we have peers subscribe to the topic
|
||||
# but not being part of our mesh peers (as these peers are the peers to GRAFT).
|
||||
# So I monkeypatch the peer subscriptions and our mesh peers.
|
||||
total_peer_count = 28
|
||||
topic_mesh = "TEST_GOSSIP_HEARTBEAT_1"
|
||||
topic_fanout = "TEST_GOSSIP_HEARTBEAT_2"
|
||||
|
||||
fake_peer_ids = [IDFactory() for _ in range(total_peer_count)]
|
||||
peer_protocol = {peer_id: PROTOCOL_ID for peer_id in fake_peer_ids}
|
||||
monkeypatch.setattr(pubsubs_gsub[0].router, "peer_protocol", peer_protocol)
|
||||
|
||||
topic_mesh_peer_count = 14
|
||||
# Split into mesh peers and fanout peers
|
||||
peer_topics = {
|
||||
topic_mesh: set(fake_peer_ids[:topic_mesh_peer_count]),
|
||||
topic_fanout: set(fake_peer_ids[topic_mesh_peer_count:]),
|
||||
}
|
||||
# Monkeypatch the peer subscriptions
|
||||
monkeypatch.setattr(pubsubs_gsub[0], "peer_topics", peer_topics)
|
||||
|
||||
mesh_peer_indices = random.sample(
|
||||
range(topic_mesh_peer_count), initial_peer_count
|
||||
)
|
||||
mesh_peers = [fake_peer_ids[i] for i in mesh_peer_indices]
|
||||
router_mesh = {topic_mesh: set(mesh_peers)}
|
||||
# Monkeypatch our mesh peers
|
||||
monkeypatch.setattr(pubsubs_gsub[0].router, "mesh", router_mesh)
|
||||
fanout_peer_indices = random.sample(
|
||||
range(topic_mesh_peer_count, total_peer_count), initial_peer_count
|
||||
)
|
||||
fanout_peers = [fake_peer_ids[i] for i in fanout_peer_indices]
|
||||
router_fanout = {topic_fanout: set(fanout_peers)}
|
||||
# Monkeypatch our fanout peers
|
||||
monkeypatch.setattr(pubsubs_gsub[0].router, "fanout", router_fanout)
|
||||
|
||||
def window(topic):
|
||||
if topic == topic_mesh:
|
||||
return [topic_mesh]
|
||||
elif topic == topic_fanout:
|
||||
return [topic_fanout]
|
||||
else:
|
||||
return []
|
||||
|
||||
# Monkeypatch the memory cache messages
|
||||
monkeypatch.setattr(pubsubs_gsub[0].router.mcache, "window", window)
|
||||
|
||||
peers_to_gossip = pubsubs_gsub[0].router.gossip_heartbeat()
|
||||
# If our mesh peer count is less than `GossipSubDegree`, we should gossip to up to
|
||||
# `GossipSubDegree` peers (exclude mesh peers).
|
||||
if topic_mesh_peer_count - initial_peer_count < pubsubs_gsub[0].router.degree:
|
||||
# The same goes for fanout so it's two times the number of peers to gossip.
|
||||
assert len(peers_to_gossip) == 2 * (
|
||||
topic_mesh_peer_count - initial_peer_count
|
||||
)
|
||||
elif (
|
||||
topic_mesh_peer_count - initial_peer_count >= pubsubs_gsub[0].router.degree
|
||||
):
|
||||
assert len(peers_to_gossip) == 2 * (pubsubs_gsub[0].router.degree)
|
||||
|
||||
for peer in peers_to_gossip:
|
||||
if peer in peer_topics[topic_mesh]:
|
||||
# Check that the peer to gossip to is not in our mesh peers
|
||||
assert peer not in mesh_peers
|
||||
assert topic_mesh in peers_to_gossip[peer]
|
||||
elif peer in peer_topics[topic_fanout]:
|
||||
# Check that the peer to gossip to is not in our fanout peers
|
||||
assert peer not in fanout_peers
|
||||
assert topic_fanout in peers_to_gossip[peer]
|
||||
|
||||
@ -6,6 +6,7 @@ import trio
|
||||
|
||||
from libp2p.exceptions import ValidationError
|
||||
from libp2p.pubsub.pb import rpc_pb2
|
||||
from libp2p.pubsub.pubsub import PUBSUB_SIGNING_PREFIX
|
||||
from libp2p.tools.constants import MAX_READ_LEN
|
||||
from libp2p.tools.factories import IDFactory, PubsubFactory, net_stream_pair_factory
|
||||
from libp2p.tools.pubsub.utils import make_pubsub_msg
|
||||
@ -60,11 +61,11 @@ async def test_peers_subscribe():
|
||||
await connect(pubsubs_fsub[0].host, pubsubs_fsub[1].host)
|
||||
await pubsubs_fsub[0].subscribe(TESTING_TOPIC)
|
||||
# Yield to let 0 notify 1
|
||||
await trio.sleep(0.1)
|
||||
await trio.sleep(1)
|
||||
assert pubsubs_fsub[0].my_id in pubsubs_fsub[1].peer_topics[TESTING_TOPIC]
|
||||
await pubsubs_fsub[0].unsubscribe(TESTING_TOPIC)
|
||||
# Yield to let 0 notify 1
|
||||
await trio.sleep(0.1)
|
||||
await trio.sleep(1)
|
||||
assert pubsubs_fsub[0].my_id not in pubsubs_fsub[1].peer_topics[TESTING_TOPIC]
|
||||
|
||||
|
||||
@ -509,3 +510,76 @@ async def test_push_msg(monkeypatch):
|
||||
await pubsubs_fsub[0].push_msg(pubsubs_fsub[0].my_id, msg_2)
|
||||
await trio.sleep(0.01)
|
||||
assert not event.is_set()
|
||||
|
||||
|
||||
@pytest.mark.trio
|
||||
async def test_strict_signing():
|
||||
async with PubsubFactory.create_batch_with_floodsub(
|
||||
2, strict_signing=True
|
||||
) as pubsubs_fsub:
|
||||
await connect(pubsubs_fsub[0].host, pubsubs_fsub[1].host)
|
||||
await pubsubs_fsub[0].subscribe(TESTING_TOPIC)
|
||||
await pubsubs_fsub[1].subscribe(TESTING_TOPIC)
|
||||
await trio.sleep(1)
|
||||
|
||||
await pubsubs_fsub[0].publish(TESTING_TOPIC, TESTING_DATA)
|
||||
await trio.sleep(1)
|
||||
|
||||
assert len(pubsubs_fsub[0].seen_messages) == 1
|
||||
assert len(pubsubs_fsub[1].seen_messages) == 1
|
||||
|
||||
|
||||
@pytest.mark.trio
|
||||
async def test_strict_signing_failed_validation(monkeypatch):
|
||||
async with PubsubFactory.create_batch_with_floodsub(
|
||||
2, strict_signing=True
|
||||
) as pubsubs_fsub:
|
||||
msg = make_pubsub_msg(
|
||||
origin_id=pubsubs_fsub[0].my_id,
|
||||
topic_ids=[TESTING_TOPIC],
|
||||
data=TESTING_DATA,
|
||||
seqno=b"\x00" * 8,
|
||||
)
|
||||
priv_key = pubsubs_fsub[0].sign_key
|
||||
signature = priv_key.sign(
|
||||
PUBSUB_SIGNING_PREFIX.encode() + msg.SerializeToString()
|
||||
)
|
||||
|
||||
event = trio.Event()
|
||||
|
||||
def _is_msg_seen(msg):
|
||||
return False
|
||||
|
||||
# Use router publish to check if `push_msg` succeed.
|
||||
async def router_publish(*args, **kwargs):
|
||||
# The event will only be set if `push_msg` succeed.
|
||||
event.set()
|
||||
|
||||
monkeypatch.setattr(pubsubs_fsub[0], "_is_msg_seen", _is_msg_seen)
|
||||
monkeypatch.setattr(pubsubs_fsub[0].router, "publish", router_publish)
|
||||
|
||||
# Test: no signature attached in `msg`
|
||||
await pubsubs_fsub[0].push_msg(pubsubs_fsub[0].my_id, msg)
|
||||
await trio.sleep(0.01)
|
||||
assert not event.is_set()
|
||||
|
||||
# Test: `msg.key` does not match `msg.from_id`
|
||||
msg.key = pubsubs_fsub[1].host.get_public_key().serialize()
|
||||
msg.signature = signature
|
||||
await pubsubs_fsub[0].push_msg(pubsubs_fsub[0].my_id, msg)
|
||||
await trio.sleep(0.01)
|
||||
assert not event.is_set()
|
||||
|
||||
# Test: invalid signature
|
||||
msg.key = pubsubs_fsub[0].host.get_public_key().serialize()
|
||||
msg.signature = b"\x12" * 100
|
||||
await pubsubs_fsub[0].push_msg(pubsubs_fsub[0].my_id, msg)
|
||||
await trio.sleep(0.01)
|
||||
assert not event.is_set()
|
||||
|
||||
# Finally, assert the signature indeed will pass validation
|
||||
msg.key = pubsubs_fsub[0].host.get_public_key().serialize()
|
||||
msg.signature = signature
|
||||
await pubsubs_fsub[0].push_msg(pubsubs_fsub[0].my_id, msg)
|
||||
await trio.sleep(0.01)
|
||||
assert event.is_set()
|
||||
|
||||
@ -1,75 +0,0 @@
|
||||
import pytest
|
||||
|
||||
from libp2p.kademlia.network import KademliaServer
|
||||
from libp2p.peer.id import ID
|
||||
from libp2p.routing.kademlia.kademlia_peer_router import (
|
||||
KadmeliaPeerRouter,
|
||||
peer_info_to_str,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_simple_two_nodes():
|
||||
node_a = KademliaServer()
|
||||
await node_a.listen(5678)
|
||||
|
||||
node_b = KademliaServer()
|
||||
await node_b.listen(5679)
|
||||
|
||||
node_a_value = await node_b.bootstrap([("127.0.0.1", 5678)])
|
||||
node_a_kad_peerinfo = node_a_value[0]
|
||||
await node_a.set(node_a_kad_peerinfo.xor_id, peer_info_to_str(node_a_kad_peerinfo))
|
||||
|
||||
router = KadmeliaPeerRouter(node_b)
|
||||
returned_info = await router.find_peer(ID(node_a_kad_peerinfo.peer_id_bytes))
|
||||
assert returned_info == node_a_kad_peerinfo
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_simple_three_nodes():
|
||||
node_a = KademliaServer()
|
||||
await node_a.listen(5701)
|
||||
|
||||
node_b = KademliaServer()
|
||||
await node_b.listen(5702)
|
||||
|
||||
node_c = KademliaServer()
|
||||
await node_c.listen(5703)
|
||||
|
||||
node_a_value = await node_b.bootstrap([("127.0.0.1", 5701)])
|
||||
node_a_kad_peerinfo = node_a_value[0]
|
||||
|
||||
await node_c.bootstrap([("127.0.0.1", 5702)])
|
||||
await node_a.set(node_a_kad_peerinfo.xor_id, peer_info_to_str(node_a_kad_peerinfo))
|
||||
|
||||
router = KadmeliaPeerRouter(node_c)
|
||||
returned_info = await router.find_peer(ID(node_a_kad_peerinfo.peer_id_bytes))
|
||||
assert returned_info == node_a_kad_peerinfo
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_simple_four_nodes():
|
||||
node_a = KademliaServer()
|
||||
await node_a.listen(5801)
|
||||
|
||||
node_b = KademliaServer()
|
||||
await node_b.listen(5802)
|
||||
|
||||
node_c = KademliaServer()
|
||||
await node_c.listen(5803)
|
||||
|
||||
node_d = KademliaServer()
|
||||
await node_d.listen(5804)
|
||||
|
||||
node_a_value = await node_b.bootstrap([("127.0.0.1", 5801)])
|
||||
node_a_kad_peerinfo = node_a_value[0]
|
||||
|
||||
await node_c.bootstrap([("127.0.0.1", 5802)])
|
||||
|
||||
await node_d.bootstrap([("127.0.0.1", 5803)])
|
||||
|
||||
await node_b.set(node_a_kad_peerinfo.xor_id, peer_info_to_str(node_a_kad_peerinfo))
|
||||
|
||||
router = KadmeliaPeerRouter(node_d)
|
||||
returned_info = await router.find_peer(ID(node_a_kad_peerinfo.peer_id_bytes))
|
||||
assert returned_info == node_a_kad_peerinfo
|
||||
@ -4,6 +4,7 @@ import trio
|
||||
|
||||
from libp2p.network.connection.raw_connection import RawConnection
|
||||
from libp2p.tools.constants import LISTEN_MADDR
|
||||
from libp2p.transport.exceptions import OpenConnectionError
|
||||
from libp2p.transport.tcp.tcp import TCP
|
||||
|
||||
|
||||
@ -26,14 +27,17 @@ async def test_tcp_listener(nursery):
|
||||
async def test_tcp_dial(nursery):
|
||||
transport = TCP()
|
||||
raw_conn_other_side = None
|
||||
event = trio.Event()
|
||||
|
||||
async def handler(tcp_stream):
|
||||
nonlocal raw_conn_other_side
|
||||
raw_conn_other_side = RawConnection(tcp_stream, False)
|
||||
event.set()
|
||||
await trio.sleep_forever()
|
||||
|
||||
# Test: OSError is raised when trying to dial to a port which no one is not listening to.
|
||||
with pytest.raises(OSError):
|
||||
# Test: `OpenConnectionError` is raised when trying to dial to a port which
|
||||
# no one is not listening to.
|
||||
with pytest.raises(OpenConnectionError):
|
||||
await transport.dial(Multiaddr("/ip4/127.0.0.1/tcp/1"))
|
||||
|
||||
listener = transport.create_listener(handler)
|
||||
@ -42,6 +46,7 @@ async def test_tcp_dial(nursery):
|
||||
assert len(addrs) == 1
|
||||
listen_addr = addrs[0]
|
||||
raw_conn = await transport.dial(listen_addr)
|
||||
await event.wait()
|
||||
|
||||
data = b"123"
|
||||
await raw_conn_other_side.write(data)
|
||||
|
||||
Reference in New Issue
Block a user