diff --git a/libp2p/security/security_multistream.py b/libp2p/security/security_multistream.py index 0507a524..c83a2aa4 100644 --- a/libp2p/security/security_multistream.py +++ b/libp2p/security/security_multistream.py @@ -27,7 +27,6 @@ class SecurityMultistream(ABC): Go implementation: github.com/libp2p/go-conn-security-multistream/ssms.go """ - # NOTE: Can be changed to `typing.OrderedDict` since Python 3.7.2. transports: "OrderedDict[TProtocol, ISecureTransport]" multiselect: Multiselect multiselect_client: MultiselectClient diff --git a/libp2p/tools/factories.py b/libp2p/tools/factories.py index 7fad8360..a97c8889 100644 --- a/libp2p/tools/factories.py +++ b/libp2p/tools/factories.py @@ -1,4 +1,4 @@ -from typing import Any, AsyncIterator, Dict, List, Sequence, Tuple, cast +from typing import Any, AsyncIterator, Callable, Dict, List, Sequence, Tuple, cast from async_exit_stack import AsyncExitStack from async_generator import asynccontextmanager @@ -33,6 +33,7 @@ from libp2p.security.noise.messages import ( NoiseHandshakePayload, make_handshake_payload_sig, ) +from libp2p.security.noise.transport import PROTOCOL_ID as NOISE_PROTOCOL_ID from libp2p.security.noise.transport import Transport as NoiseTransport import libp2p.security.secio.transport as secio from libp2p.security.secure_conn_interface import ISecureConn @@ -41,20 +42,26 @@ from libp2p.stream_muxer.mplex.mplex import MPLEX_PROTOCOL_ID, Mplex from libp2p.stream_muxer.mplex.mplex_stream import MplexStream from libp2p.tools.constants import GOSSIPSUB_PARAMS from libp2p.transport.tcp.tcp import TCP -from libp2p.transport.typing import TMuxerOptions +from libp2p.transport.typing import TMuxerOptions, TSecurityOptions from libp2p.transport.upgrader import TransportUpgrader from libp2p.typing import TProtocol from .constants import FLOODSUB_PROTOCOL_ID, GOSSIPSUB_PROTOCOL_ID, LISTEN_MADDR from .utils import connect, connect_swarm +DEFAULT_SECURITY_PROTOCOL_ID = PLAINTEXT_PROTOCOL_ID + + +def default_key_pair_factory() -> KeyPair: + return generate_new_rsa_identity() + class IDFactory(factory.Factory): class Meta: model = ID peer_id_bytes = factory.LazyFunction( - lambda: generate_peer_id_from(generate_new_rsa_identity()) + lambda: generate_peer_id_from(default_key_pair_factory()) ) @@ -64,15 +71,6 @@ def initialize_peerstore_with_our_keypair(self_id: ID, key_pair: KeyPair) -> Pee return peer_store -def security_transport_factory( - is_secure: bool, key_pair: KeyPair -) -> Dict[TProtocol, ISecureTransport]: - if not is_secure: - return {PLAINTEXT_PROTOCOL_ID: InsecureTransport(key_pair)} - else: - return {secio.ID: secio.Transport(key_pair)} - - def noise_static_key_factory() -> PrivateKey: return create_ed25519_key_pair().private_key @@ -88,15 +86,52 @@ def noise_handshake_payload_factory() -> NoiseHandshakePayload: ) -def noise_transport_factory() -> NoiseTransport: +def plaintext_transport_factory(key_pair: KeyPair) -> ISecureTransport: + return InsecureTransport(key_pair) + + +def secio_transport_factory(key_pair: KeyPair) -> ISecureTransport: + return secio.Transport(key_pair) + + +def noise_transport_factory(key_pair: KeyPair) -> ISecureTransport: return NoiseTransport( - libp2p_keypair=create_secp256k1_key_pair(), + libp2p_keypair=key_pair, noise_privkey=noise_static_key_factory(), early_data=None, with_noise_pipes=False, ) +def security_options_factory_factory( + protocol_id: TProtocol = None +) -> Callable[[KeyPair], TSecurityOptions]: + if protocol_id is None: + protocol_id = DEFAULT_SECURITY_PROTOCOL_ID + + def security_options_factory(key_pair: KeyPair) -> TSecurityOptions: + transport_factory: Callable[[KeyPair], ISecureTransport] + if protocol_id == PLAINTEXT_PROTOCOL_ID: + transport_factory = plaintext_transport_factory + elif protocol_id == secio.ID: + transport_factory = secio_transport_factory + elif protocol_id == NOISE_PROTOCOL_ID: + transport_factory = noise_transport_factory + else: + raise Exception(f"security transport {protocol_id} is not supported") + return {protocol_id: transport_factory(key_pair)} + + return security_options_factory + + +def mplex_transport_factory() -> TMuxerOptions: + return {MPLEX_PROTOCOL_ID: Mplex} + + +def default_muxer_transport_factory() -> TMuxerOptions: + return mplex_transport_factory() + + @asynccontextmanager async def raw_conn_factory( nursery: trio.Nursery @@ -124,8 +159,12 @@ async def raw_conn_factory( async def noise_conn_factory( nursery: trio.Nursery ) -> AsyncIterator[Tuple[ISecureConn, ISecureConn]]: - local_transport = noise_transport_factory() - remote_transport = noise_transport_factory() + local_transport = cast( + NoiseTransport, noise_transport_factory(create_secp256k1_key_pair()) + ) + remote_transport = cast( + NoiseTransport, noise_transport_factory(create_secp256k1_key_pair()) + ) local_secure_conn: ISecureConn = None remote_secure_conn: ISecureConn = None @@ -158,9 +197,9 @@ class SwarmFactory(factory.Factory): model = Swarm class Params: - is_secure = False - key_pair = factory.LazyFunction(generate_new_rsa_identity) - muxer_opt = {MPLEX_PROTOCOL_ID: Mplex} + key_pair = factory.LazyFunction(default_key_pair_factory) + security_protocol = DEFAULT_SECURITY_PROTOCOL_ID + muxer_opt = factory.LazyFunction(default_muxer_transport_factory) peer_id = factory.LazyAttribute(lambda o: generate_peer_id_from(o.key_pair)) peerstore = factory.LazyAttribute( @@ -168,7 +207,8 @@ class SwarmFactory(factory.Factory): ) upgrader = factory.LazyAttribute( lambda o: TransportUpgrader( - security_transport_factory(o.is_secure, o.key_pair), o.muxer_opt + (security_options_factory_factory(o.security_protocol))(o.key_pair), + o.muxer_opt, ) ) transport = factory.LazyFunction(TCP) @@ -176,7 +216,10 @@ class SwarmFactory(factory.Factory): @classmethod @asynccontextmanager async def create_and_listen( - cls, is_secure: bool, key_pair: KeyPair = None, muxer_opt: TMuxerOptions = None + cls, + key_pair: KeyPair = None, + security_protocol: TProtocol = None, + muxer_opt: TMuxerOptions = None, ) -> AsyncIterator[Swarm]: # `factory.Factory.__init__` does *not* prepare a *default value* if we pass # an argument explicitly with `None`. If an argument is `None`, we don't pass it to @@ -184,9 +227,11 @@ class SwarmFactory(factory.Factory): optional_kwargs: Dict[str, Any] = {} if key_pair is not None: optional_kwargs["key_pair"] = key_pair + if security_protocol is not None: + optional_kwargs["security_protocol"] = security_protocol if muxer_opt is not None: optional_kwargs["muxer_opt"] = muxer_opt - swarm = cls(is_secure=is_secure, **optional_kwargs) + swarm = cls(**optional_kwargs) async with background_trio_service(swarm): await swarm.listen(LISTEN_MADDR) yield swarm @@ -194,12 +239,17 @@ class SwarmFactory(factory.Factory): @classmethod @asynccontextmanager async def create_batch_and_listen( - cls, is_secure: bool, number: int, muxer_opt: TMuxerOptions = None + cls, + number: int, + security_protocol: TProtocol = None, + muxer_opt: TMuxerOptions = None, ) -> AsyncIterator[Tuple[Swarm, ...]]: async with AsyncExitStack() as stack: ctx_mgrs = [ await stack.enter_async_context( - cls.create_and_listen(is_secure=is_secure, muxer_opt=muxer_opt) + cls.create_and_listen( + security_protocol=security_protocol, muxer_opt=muxer_opt + ) ) for _ in range(number) ] @@ -211,17 +261,27 @@ class HostFactory(factory.Factory): model = BasicHost class Params: - is_secure = False - key_pair = factory.LazyFunction(generate_new_rsa_identity) + key_pair = factory.LazyFunction(default_key_pair_factory) + security_protocol: TProtocol = None + muxer_opt = factory.LazyFunction(default_muxer_transport_factory) - network = factory.LazyAttribute(lambda o: SwarmFactory(is_secure=o.is_secure)) + network = factory.LazyAttribute( + lambda o: SwarmFactory( + security_protocol=o.security_protocol, muxer_opt=o.muxer_opt + ) + ) @classmethod @asynccontextmanager async def create_batch_and_listen( - cls, is_secure: bool, number: int + cls, + number: int, + security_protocol: TProtocol = None, + muxer_opt: TMuxerOptions = None, ) -> AsyncIterator[Tuple[BasicHost, ...]]: - async with SwarmFactory.create_batch_and_listen(is_secure, number) as swarms: + async with SwarmFactory.create_batch_and_listen( + number, security_protocol=security_protocol, muxer_opt=muxer_opt + ) as swarms: hosts = tuple(BasicHost(swarm) for swarm in swarms) yield hosts @@ -245,20 +305,29 @@ class RoutedHostFactory(factory.Factory): model = RoutedHost class Params: - is_secure = False + key_pair = factory.LazyFunction(default_key_pair_factory) + security_protocol: TProtocol = None + muxer_opt = factory.LazyFunction(default_muxer_transport_factory) network = factory.LazyAttribute( - lambda o: HostFactory(is_secure=o.is_secure).get_network() + lambda o: HostFactory( + security_protocol=o.security_protocol, muxer_opt=o.muxer_opt + ).get_network() ) router = factory.LazyFunction(DummyRouter) @classmethod @asynccontextmanager async def create_batch_and_listen( - cls, is_secure: bool, number: int + cls, + number: int, + security_protocol: TProtocol = None, + muxer_opt: TMuxerOptions = None, ) -> AsyncIterator[Tuple[RoutedHost, ...]]: routing_table = DummyRouter() - async with HostFactory.create_batch_and_listen(is_secure, number) as hosts: + async with HostFactory.create_batch_and_listen( + number, security_protocol=security_protocol, muxer_opt=muxer_opt + ) as hosts: for host in hosts: routing_table._add_peer(host.get_id(), host.get_addrs()) routed_hosts = tuple( @@ -319,11 +388,14 @@ class PubsubFactory(factory.Factory): cls, number: int, routers: Sequence[IPubsubRouter], - is_secure: bool = False, cache_size: int = None, strict_signing: bool = False, + security_protocol: TProtocol = None, + muxer_opt: TMuxerOptions = None, ) -> AsyncIterator[Tuple[Pubsub, ...]]: - async with HostFactory.create_batch_and_listen(is_secure, number) as hosts: + async with HostFactory.create_batch_and_listen( + number, security_protocol=security_protocol, muxer_opt=muxer_opt + ) as hosts: # Pubsubs should exit before hosts async with AsyncExitStack() as stack: pubsubs = [ @@ -339,17 +411,23 @@ class PubsubFactory(factory.Factory): async def create_batch_with_floodsub( cls, number: int, - is_secure: bool = False, cache_size: int = None, strict_signing: bool = False, protocols: Sequence[TProtocol] = None, + security_protocol: TProtocol = None, + muxer_opt: TMuxerOptions = None, ) -> AsyncIterator[Tuple[Pubsub, ...]]: if protocols is not None: floodsubs = FloodsubFactory.create_batch(number, protocols=list(protocols)) else: floodsubs = FloodsubFactory.create_batch(number) async with cls._create_batch_with_router( - number, floodsubs, is_secure, cache_size, strict_signing + number, + floodsubs, + cache_size, + strict_signing, + security_protocol=security_protocol, + muxer_opt=muxer_opt, ) as pubsubs: yield pubsubs @@ -359,7 +437,6 @@ class PubsubFactory(factory.Factory): cls, number: int, *, - is_secure: bool = False, cache_size: int = None, strict_signing: bool = False, protocols: Sequence[TProtocol] = None, @@ -371,6 +448,8 @@ class PubsubFactory(factory.Factory): gossip_history: int = GOSSIPSUB_PARAMS.gossip_history, heartbeat_interval: float = GOSSIPSUB_PARAMS.heartbeat_interval, heartbeat_initial_delay: float = GOSSIPSUB_PARAMS.heartbeat_initial_delay, + security_protocol: TProtocol = None, + muxer_opt: TMuxerOptions = None, ) -> AsyncIterator[Tuple[Pubsub, ...]]: if protocols is not None: gossipsubs = GossipsubFactory.create_batch( @@ -395,7 +474,12 @@ class PubsubFactory(factory.Factory): ) async with cls._create_batch_with_router( - number, gossipsubs, is_secure, cache_size, strict_signing + number, + gossipsubs, + cache_size, + strict_signing, + security_protocol=security_protocol, + muxer_opt=muxer_opt, ) as pubsubs: async with AsyncExitStack() as stack: for router in gossipsubs: @@ -405,10 +489,10 @@ class PubsubFactory(factory.Factory): @asynccontextmanager async def swarm_pair_factory( - is_secure: bool, muxer_opt: TMuxerOptions = None + security_protocol: TProtocol = None, muxer_opt: TMuxerOptions = None ) -> AsyncIterator[Tuple[Swarm, Swarm]]: async with SwarmFactory.create_batch_and_listen( - is_secure, 2, muxer_opt=muxer_opt + 2, security_protocol=security_protocol, muxer_opt=muxer_opt ) as swarms: await connect_swarm(swarms[0], swarms[1]) yield swarms[0], swarms[1] @@ -416,18 +500,22 @@ async def swarm_pair_factory( @asynccontextmanager async def host_pair_factory( - is_secure: bool + security_protocol: TProtocol = None, muxer_opt: TMuxerOptions = None ) -> AsyncIterator[Tuple[BasicHost, BasicHost]]: - async with HostFactory.create_batch_and_listen(is_secure, 2) as hosts: + async with HostFactory.create_batch_and_listen( + 2, security_protocol=security_protocol, muxer_opt=muxer_opt + ) as hosts: await connect(hosts[0], hosts[1]) yield hosts[0], hosts[1] @asynccontextmanager async def swarm_conn_pair_factory( - is_secure: bool, muxer_opt: TMuxerOptions = None + security_protocol: TProtocol = None, muxer_opt: TMuxerOptions = None ) -> AsyncIterator[Tuple[SwarmConn, SwarmConn]]: - async with swarm_pair_factory(is_secure) as swarms: + async with swarm_pair_factory( + security_protocol=security_protocol, muxer_opt=muxer_opt + ) as swarms: conn_0 = swarms[0].connections[swarms[1].get_peer_id()] conn_1 = swarms[1].connections[swarms[0].get_peer_id()] yield cast(SwarmConn, conn_0), cast(SwarmConn, conn_1) @@ -435,10 +523,11 @@ async def swarm_conn_pair_factory( @asynccontextmanager async def mplex_conn_pair_factory( - is_secure: bool + security_protocol: TProtocol = None ) -> AsyncIterator[Tuple[Mplex, Mplex]]: - muxer_opt = {MPLEX_PROTOCOL_ID: Mplex} - async with swarm_conn_pair_factory(is_secure, muxer_opt=muxer_opt) as swarm_pair: + async with swarm_conn_pair_factory( + security_protocol=security_protocol, muxer_opt=default_muxer_transport_factory() + ) as swarm_pair: yield ( cast(Mplex, swarm_pair[0].muxed_conn), cast(Mplex, swarm_pair[1].muxed_conn), @@ -447,9 +536,11 @@ async def mplex_conn_pair_factory( @asynccontextmanager async def mplex_stream_pair_factory( - is_secure: bool + security_protocol: TProtocol = None ) -> AsyncIterator[Tuple[MplexStream, MplexStream]]: - async with mplex_conn_pair_factory(is_secure) as mplex_conn_pair_info: + async with mplex_conn_pair_factory( + security_protocol=security_protocol + ) as mplex_conn_pair_info: mplex_conn_0, mplex_conn_1 = mplex_conn_pair_info stream_0 = cast(MplexStream, await mplex_conn_0.open_stream()) await trio.sleep(0.01) @@ -463,7 +554,7 @@ async def mplex_stream_pair_factory( @asynccontextmanager async def net_stream_pair_factory( - is_secure: bool + security_protocol: TProtocol = None, muxer_opt: TMuxerOptions = None ) -> AsyncIterator[Tuple[INetStream, INetStream]]: protocol_id = TProtocol("/example/id/1") @@ -478,7 +569,9 @@ async def net_stream_pair_factory( stream_1 = stream await event_handler_finished.wait() - async with host_pair_factory(is_secure) as hosts: + async with host_pair_factory( + security_protocol=security_protocol, muxer_opt=muxer_opt + ) as hosts: hosts[1].set_stream_handler(protocol_id, handler) stream_0 = await hosts[0].new_stream(hosts[1].get_id(), [protocol_id]) diff --git a/libp2p/tools/interop/daemon.py b/libp2p/tools/interop/daemon.py index 3344255a..4dfcfb63 100644 --- a/libp2p/tools/interop/daemon.py +++ b/libp2p/tools/interop/daemon.py @@ -8,6 +8,8 @@ import trio from libp2p.peer.id import ID from libp2p.peer.peerinfo import PeerInfo, info_from_p2p_addr +from libp2p.security.insecure.transport import PLAINTEXT_PROTOCOL_ID +from libp2p.typing import TProtocol from .constants import LOCALHOST_IP from .envs import GO_BIN_PATH @@ -20,7 +22,7 @@ class P2PDProcess(BaseInteractiveProcess): def __init__( self, control_maddr: Multiaddr, - is_secure: bool, + security_protocol: TProtocol, is_pubsub_enabled: bool = True, is_gossipsub: bool = True, is_pubsub_signing: bool = False, @@ -28,7 +30,7 @@ class P2PDProcess(BaseInteractiveProcess): ) -> None: args = [f"-listen={control_maddr!s}"] # NOTE: To support `-insecure`, we need to hack `go-libp2p-daemon`. - if not is_secure: + if security_protocol == PLAINTEXT_PROTOCOL_ID: args.append("-insecure=true") if is_pubsub_enabled: args.append("-pubsub") @@ -85,7 +87,7 @@ class Daemon: async def make_p2pd( daemon_control_port: int, client_callback_port: int, - is_secure: bool, + security_protocol: TProtocol, is_pubsub_enabled: bool = True, is_gossipsub: bool = True, is_pubsub_signing: bool = False, @@ -94,7 +96,7 @@ async def make_p2pd( control_maddr = Multiaddr(f"/ip4/{LOCALHOST_IP}/tcp/{daemon_control_port}") p2pd_proc = P2PDProcess( control_maddr, - is_secure, + security_protocol, is_pubsub_enabled, is_gossipsub, is_pubsub_signing, diff --git a/tests/conftest.py b/tests/conftest.py index 48d705c2..c1a2b132 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -4,8 +4,8 @@ from libp2p.tools.factories import HostFactory @pytest.fixture -def is_host_secure(): - return False +def security_protocol(): + return None @pytest.fixture @@ -14,6 +14,8 @@ def num_hosts(): @pytest.fixture -async def hosts(num_hosts, is_host_secure, nursery): - async with HostFactory.create_batch_and_listen(is_host_secure, num_hosts) as _hosts: +async def hosts(num_hosts, security_protocol, nursery): + async with HostFactory.create_batch_and_listen( + num_hosts, security_protocol=security_protocol + ) as _hosts: yield _hosts diff --git a/tests/examples/test_examples.py b/tests/examples/test_examples.py index c8ce9ed8..df59bd24 100644 --- a/tests/examples/test_examples.py +++ b/tests/examples/test_examples.py @@ -92,8 +92,11 @@ async def no_common_protocol(host_a, host_b): "test", [(hello_world), (connect_write), (connect_read), (no_common_protocol)] ) @pytest.mark.trio -async def test_chat(test, is_host_secure): - async with HostFactory.create_batch_and_listen(is_host_secure, 2) as hosts: +async def test_chat(test, security_protocol): + print("!@# ", security_protocol) + async with HostFactory.create_batch_and_listen( + 2, security_protocol=security_protocol + ) as hosts: addr = hosts[0].get_addrs()[0] info = info_from_p2p_addr(addr) await hosts[1].connect(info) diff --git a/tests/host/test_ping.py b/tests/host/test_ping.py index 7a0f8db5..79285bc8 100644 --- a/tests/host/test_ping.py +++ b/tests/host/test_ping.py @@ -8,8 +8,11 @@ from libp2p.tools.factories import host_pair_factory @pytest.mark.trio -async def test_ping_once(is_host_secure): - async with host_pair_factory(is_host_secure) as (host_a, host_b): +async def test_ping_once(security_protocol): + async with host_pair_factory(security_protocol=security_protocol) as ( + host_a, + host_b, + ): stream = await host_b.new_stream(host_a.get_id(), (ID,)) some_ping = secrets.token_bytes(PING_LENGTH) await stream.write(some_ping) @@ -23,8 +26,11 @@ SOME_PING_COUNT = 3 @pytest.mark.trio -async def test_ping_several(is_host_secure): - async with host_pair_factory(is_host_secure) as (host_a, host_b): +async def test_ping_several(security_protocol): + async with host_pair_factory(security_protocol=security_protocol) as ( + host_a, + host_b, + ): stream = await host_b.new_stream(host_a.get_id(), (ID,)) for _ in range(SOME_PING_COUNT): some_ping = secrets.token_bytes(PING_LENGTH) diff --git a/tests/host/test_routed_host.py b/tests/host/test_routed_host.py index 4cfed6bf..7dfa8e7f 100644 --- a/tests/host/test_routed_host.py +++ b/tests/host/test_routed_host.py @@ -7,7 +7,7 @@ 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: + async with RoutedHostFactory.create_batch_and_listen(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(), [])) @@ -15,10 +15,9 @@ async def test_host_routing_success(): @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: + 2 + ) as routed_hosts, HostFactory.create_batch_and_listen(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(), [])) diff --git a/tests/identity/identify/test_protocol.py b/tests/identity/identify/test_protocol.py index 4bbdbcba..0d398ff9 100644 --- a/tests/identity/identify/test_protocol.py +++ b/tests/identity/identify/test_protocol.py @@ -6,8 +6,11 @@ from libp2p.tools.factories import host_pair_factory @pytest.mark.trio -async def test_identify_protocol(is_host_secure): - async with host_pair_factory(is_host_secure) as (host_a, host_b): +async def test_identify_protocol(security_protocol): + async with host_pair_factory(security_protocol=security_protocol) as ( + host_a, + host_b, + ): stream = await host_b.new_stream(host_a.get_id(), (ID,)) response = await stream.read() await stream.close() diff --git a/tests/libp2p/test_libp2p.py b/tests/libp2p/test_libp2p.py index 99a60bd5..8cf8e069 100644 --- a/tests/libp2p/test_libp2p.py +++ b/tests/libp2p/test_libp2p.py @@ -19,8 +19,10 @@ ACK_STR_3 = "ack_3:" @pytest.mark.trio -async def test_simple_messages(is_host_secure): - async with HostFactory.create_batch_and_listen(is_host_secure, 2) as hosts: +async def test_simple_messages(security_protocol): + async with HostFactory.create_batch_and_listen( + 2, security_protocol=security_protocol + ) as hosts: hosts[1].set_stream_handler( PROTOCOL_ID_0, create_echo_stream_handler(ACK_STR_0) ) @@ -38,8 +40,10 @@ async def test_simple_messages(is_host_secure): @pytest.mark.trio -async def test_double_response(is_host_secure): - async with HostFactory.create_batch_and_listen(is_host_secure, 2) as hosts: +async def test_double_response(security_protocol): + async with HostFactory.create_batch_and_listen( + 2, security_protocol=security_protocol + ) as hosts: async def double_response_stream_handler(stream): while True: @@ -78,11 +82,13 @@ async def test_double_response(is_host_secure): @pytest.mark.trio -async def test_multiple_streams(is_host_secure): +async def test_multiple_streams(security_protocol): # hosts[0] should be able to open a stream with hosts[1] and then vice versa. # Stream IDs should be generated uniquely so that the stream state is not overwritten - async with HostFactory.create_batch_and_listen(is_host_secure, 2) as hosts: + async with HostFactory.create_batch_and_listen( + 2, security_protocol=security_protocol + ) as hosts: hosts[0].set_stream_handler( PROTOCOL_ID_0, create_echo_stream_handler(ACK_STR_0) ) @@ -115,8 +121,10 @@ async def test_multiple_streams(is_host_secure): @pytest.mark.trio -async def test_multiple_streams_same_initiator_different_protocols(is_host_secure): - async with HostFactory.create_batch_and_listen(is_host_secure, 2) as hosts: +async def test_multiple_streams_same_initiator_different_protocols(security_protocol): + async with HostFactory.create_batch_and_listen( + 2, security_protocol=security_protocol + ) as hosts: hosts[1].set_stream_handler( PROTOCOL_ID_0, create_echo_stream_handler(ACK_STR_0) @@ -161,8 +169,10 @@ async def test_multiple_streams_same_initiator_different_protocols(is_host_secur @pytest.mark.trio -async def test_multiple_streams_two_initiators(is_host_secure): - async with HostFactory.create_batch_and_listen(is_host_secure, 2) as hosts: +async def test_multiple_streams_two_initiators(security_protocol): + async with HostFactory.create_batch_and_listen( + 2, security_protocol=security_protocol + ) as hosts: hosts[0].set_stream_handler( PROTOCOL_ID_2, create_echo_stream_handler(ACK_STR_2) ) @@ -217,8 +227,10 @@ async def test_multiple_streams_two_initiators(is_host_secure): @pytest.mark.trio -async def test_triangle_nodes_connection(is_host_secure): - async with HostFactory.create_batch_and_listen(is_host_secure, 3) as hosts: +async def test_triangle_nodes_connection(security_protocol): + async with HostFactory.create_batch_and_listen( + 3, security_protocol=security_protocol + ) as hosts: hosts[0].set_stream_handler( PROTOCOL_ID_0, create_echo_stream_handler(ACK_STR_0) @@ -268,8 +280,10 @@ 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: +async def test_host_connect(security_protocol): + async with HostFactory.create_batch_and_listen( + 2, security_protocol=security_protocol + ) as hosts: assert len(hosts[0].get_peerstore().peer_ids()) == 1 await connect(hosts[0], hosts[1]) diff --git a/tests/network/conftest.py b/tests/network/conftest.py index 5aad36c9..42ea8730 100644 --- a/tests/network/conftest.py +++ b/tests/network/conftest.py @@ -8,18 +8,22 @@ from libp2p.tools.factories import ( @pytest.fixture -async def net_stream_pair(is_host_secure): - async with net_stream_pair_factory(is_host_secure) as net_stream_pair: +async def net_stream_pair(security_protocol): + async with net_stream_pair_factory( + security_protocol=security_protocol + ) as net_stream_pair: yield net_stream_pair @pytest.fixture -async def swarm_pair(is_host_secure): - async with swarm_pair_factory(is_host_secure) as swarms: +async def swarm_pair(security_protocol): + async with swarm_pair_factory(security_protocol=security_protocol) as swarms: yield swarms @pytest.fixture -async def swarm_conn_pair(is_host_secure): - async with swarm_conn_pair_factory(is_host_secure) as swarm_conn_pair: +async def swarm_conn_pair(security_protocol): + async with swarm_conn_pair_factory( + security_protocol=security_protocol + ) as swarm_conn_pair: yield swarm_conn_pair diff --git a/tests/network/test_notify.py b/tests/network/test_notify.py index 328ff128..5be4c082 100644 --- a/tests/network/test_notify.py +++ b/tests/network/test_notify.py @@ -55,8 +55,8 @@ class MyNotifee(INotifee): @pytest.mark.trio -async def test_notify(is_host_secure): - swarms = [SwarmFactory(is_secure=is_host_secure) for _ in range(2)] +async def test_notify(security_protocol): + swarms = [SwarmFactory(security_protocol=security_protocol) for _ in range(2)] events_0_0 = [] events_1_0 = [] diff --git a/tests/network/test_swarm.py b/tests/network/test_swarm.py index 70b82477..42b7db1c 100644 --- a/tests/network/test_swarm.py +++ b/tests/network/test_swarm.py @@ -9,8 +9,10 @@ from libp2p.tools.utils import connect_swarm @pytest.mark.trio -async def test_swarm_dial_peer(is_host_secure): - async with SwarmFactory.create_batch_and_listen(is_host_secure, 3) as swarms: +async def test_swarm_dial_peer(security_protocol): + async with SwarmFactory.create_batch_and_listen( + 3, security_protocol=security_protocol + ) as swarms: # Test: No addr found. with pytest.raises(SwarmException): await swarms[0].dial_peer(swarms[1].get_peer_id()) @@ -38,8 +40,10 @@ async def test_swarm_dial_peer(is_host_secure): @pytest.mark.trio -async def test_swarm_close_peer(is_host_secure): - async with SwarmFactory.create_batch_and_listen(is_host_secure, 3) as swarms: +async def test_swarm_close_peer(security_protocol): + async with SwarmFactory.create_batch_and_listen( + 3, security_protocol=security_protocol + ) as swarms: # 0 <> 1 <> 2 await connect_swarm(swarms[0], swarms[1]) await connect_swarm(swarms[1], swarms[2]) @@ -90,8 +94,10 @@ async def test_swarm_remove_conn(swarm_pair): @pytest.mark.trio -async def test_swarm_multiaddr(is_host_secure): - async with SwarmFactory.create_batch_and_listen(is_host_secure, 3) as swarms: +async def test_swarm_multiaddr(security_protocol): + async with SwarmFactory.create_batch_and_listen( + 3, security_protocol=security_protocol + ) as swarms: def clear(): swarms[0].peerstore.clear_addrs(swarms[1].get_peer_id()) diff --git a/tests/protocol_muxer/test_protocol_muxer.py b/tests/protocol_muxer/test_protocol_muxer.py index cd82652c..0537c4ea 100644 --- a/tests/protocol_muxer/test_protocol_muxer.py +++ b/tests/protocol_muxer/test_protocol_muxer.py @@ -16,9 +16,11 @@ async def perform_simple_test( expected_selected_protocol, protocols_for_client, protocols_with_handlers, - is_host_secure, + security_protocol, ): - async with HostFactory.create_batch_and_listen(is_host_secure, 2) as hosts: + async with HostFactory.create_batch_and_listen( + 2, security_protocol=security_protocol + ) as hosts: for protocol in protocols_with_handlers: hosts[1].set_stream_handler( protocol, create_echo_stream_handler(ACK_PREFIX) @@ -38,28 +40,28 @@ async def perform_simple_test( @pytest.mark.trio -async def test_single_protocol_succeeds(is_host_secure): +async def test_single_protocol_succeeds(security_protocol): expected_selected_protocol = PROTOCOL_ECHO await perform_simple_test( expected_selected_protocol, [expected_selected_protocol], [expected_selected_protocol], - is_host_secure, + security_protocol, ) @pytest.mark.trio -async def test_single_protocol_fails(is_host_secure): +async def test_single_protocol_fails(security_protocol): with pytest.raises(StreamFailure): await perform_simple_test( - "", [PROTOCOL_ECHO], [PROTOCOL_POTATO], is_host_secure + "", [PROTOCOL_ECHO], [PROTOCOL_POTATO], security_protocol ) # Cleanup not reached on error @pytest.mark.trio -async def test_multiple_protocol_first_is_valid_succeeds(is_host_secure): +async def test_multiple_protocol_first_is_valid_succeeds(security_protocol): expected_selected_protocol = PROTOCOL_ECHO protocols_for_client = [PROTOCOL_ECHO, PROTOCOL_POTATO] protocols_for_listener = [PROTOCOL_FOO, PROTOCOL_ECHO] @@ -67,12 +69,12 @@ async def test_multiple_protocol_first_is_valid_succeeds(is_host_secure): expected_selected_protocol, protocols_for_client, protocols_for_listener, - is_host_secure, + security_protocol, ) @pytest.mark.trio -async def test_multiple_protocol_second_is_valid_succeeds(is_host_secure): +async def test_multiple_protocol_second_is_valid_succeeds(security_protocol): expected_selected_protocol = PROTOCOL_FOO protocols_for_client = [PROTOCOL_ROCK, PROTOCOL_FOO] protocols_for_listener = [PROTOCOL_FOO, PROTOCOL_ECHO] @@ -80,15 +82,15 @@ async def test_multiple_protocol_second_is_valid_succeeds(is_host_secure): expected_selected_protocol, protocols_for_client, protocols_for_listener, - is_host_secure, + security_protocol, ) @pytest.mark.trio -async def test_multiple_protocol_fails(is_host_secure): +async def test_multiple_protocol_fails(security_protocol): protocols_for_client = [PROTOCOL_ROCK, PROTOCOL_FOO, "/bar/1.0.0"] protocols_for_listener = ["/aspyn/1.0.0", "/rob/1.0.0", "/zx/1.0.0", "/alex/1.0.0"] with pytest.raises(StreamFailure): await perform_simple_test( - "", protocols_for_client, protocols_for_listener, is_host_secure + "", protocols_for_client, protocols_for_listener, security_protocol ) diff --git a/tests/pubsub/test_floodsub.py b/tests/pubsub/test_floodsub.py index 148c001b..36bf6fcd 100644 --- a/tests/pubsub/test_floodsub.py +++ b/tests/pubsub/test_floodsub.py @@ -82,10 +82,11 @@ async def test_lru_cache_two_nodes(monkeypatch): @pytest.mark.parametrize("test_case_obj", floodsub_protocol_pytest_params) @pytest.mark.trio @pytest.mark.slow -async def test_gossipsub_run_with_floodsub_tests(test_case_obj, is_host_secure): +async def test_gossipsub_run_with_floodsub_tests(test_case_obj, security_protocol): await perform_test_from_obj( test_case_obj, functools.partial( - PubsubFactory.create_batch_with_floodsub, is_secure=is_host_secure + PubsubFactory.create_batch_with_floodsub, + security_protocol=security_protocol, ), ) diff --git a/tests/pubsub/test_pubsub.py b/tests/pubsub/test_pubsub.py index d6c29310..2c6fe7dc 100644 --- a/tests/pubsub/test_pubsub.py +++ b/tests/pubsub/test_pubsub.py @@ -236,7 +236,7 @@ async def test_validate_msg(is_topic_1_val_passed, is_topic_2_val_passed): @pytest.mark.trio -async def test_continuously_read_stream(monkeypatch, nursery, is_host_secure): +async def test_continuously_read_stream(monkeypatch, nursery, security_protocol): async def wait_for_event_occurring(event): await trio.hazmat.checkpoint() with trio.fail_after(0.1): @@ -271,8 +271,10 @@ async def test_continuously_read_stream(monkeypatch, nursery, is_host_secure): yield Events(event_push_msg, event_handle_subscription, event_handle_rpc) async with PubsubFactory.create_batch_with_floodsub( - 1, is_secure=is_host_secure - ) as pubsubs_fsub, net_stream_pair_factory(is_secure=is_host_secure) as stream_pair: + 1, security_protocol=security_protocol + ) as pubsubs_fsub, net_stream_pair_factory( + security_protocol=security_protocol + ) as stream_pair: await pubsubs_fsub[0].subscribe(TESTING_TOPIC) # Kick off the task `continuously_read_stream` nursery.start_soon(pubsubs_fsub[0].continuously_read_stream, stream_pair[0]) @@ -394,10 +396,12 @@ async def test_handle_talk(): @pytest.mark.trio -async def test_message_all_peers(monkeypatch, is_host_secure): +async def test_message_all_peers(monkeypatch, security_protocol): async with PubsubFactory.create_batch_with_floodsub( - 1, is_secure=is_host_secure - ) as pubsubs_fsub, net_stream_pair_factory(is_secure=is_host_secure) as stream_pair: + 1, security_protocol=security_protocol + ) as pubsubs_fsub, net_stream_pair_factory( + security_protocol=security_protocol + ) as stream_pair: peer_id = IDFactory() mock_peers = {peer_id: stream_pair[0]} with monkeypatch.context() as m: diff --git a/tests/security/test_security_multistream.py b/tests/security/test_security_multistream.py index cd968ac2..32534cfb 100644 --- a/tests/security/test_security_multistream.py +++ b/tests/security/test_security_multistream.py @@ -1,88 +1,49 @@ import pytest -import trio -from libp2p import new_host from libp2p.crypto.rsa import create_new_key_pair -from libp2p.security.insecure.transport import InsecureSession, InsecureTransport -from libp2p.tools.constants import LISTEN_MADDR -from libp2p.tools.utils import connect - -# TODO: Add tests for multiple streams being opened on different -# protocols through the same connection - - -def peer_id_for_node(node): - return node.get_id() - +from libp2p.security.insecure.transport import PLAINTEXT_PROTOCOL_ID, InsecureSession +from libp2p.security.noise.transport import PROTOCOL_ID as NOISE_PROTOCOL_ID +from libp2p.security.secio.transport import ID as SECIO_PROTOCOL_ID +from libp2p.security.secure_session import SecureSession +from libp2p.tools.factories import host_pair_factory initiator_key_pair = create_new_key_pair() noninitiator_key_pair = create_new_key_pair() -async def perform_simple_test( - assertion_func, transports_for_initiator, transports_for_noninitiator -): - # Create libp2p nodes and connect them, then secure the connection, then check - # the proper security was chosen - # TODO: implement -- note we need to introduce the notion of communicating over a raw connection - # for testing, we do NOT want to communicate over a stream so we can't just create two nodes - # and use their conn because our mplex will internally relay messages to a stream - - node1 = new_host(key_pair=initiator_key_pair, sec_opt=transports_for_initiator) - node2 = new_host( - key_pair=noninitiator_key_pair, sec_opt=transports_for_noninitiator - ) - async with node1.run(listen_addrs=[LISTEN_MADDR]), node2.run( - listen_addrs=[LISTEN_MADDR] - ): - await connect(node1, node2) - - # Wait a very short period to allow conns to be stored (since the functions - # storing the conns are async, they may happen at slightly different times - # on each node) - await trio.sleep(0.1) - - # Get conns - node1_conn = node1.get_network().connections[peer_id_for_node(node2)] - node2_conn = node2.get_network().connections[peer_id_for_node(node1)] +async def perform_simple_test(assertion_func, security_protocol): + async with host_pair_factory(security_protocol=security_protocol) as hosts: + conn_0 = hosts[0].get_network().connections[hosts[1].get_id()] + conn_1 = hosts[1].get_network().connections[hosts[0].get_id()] # Perform assertion - assertion_func(node1_conn.muxed_conn.secured_conn) - assertion_func(node2_conn.muxed_conn.secured_conn) + assertion_func(conn_0.muxed_conn.secured_conn) + assertion_func(conn_1.muxed_conn.secured_conn) @pytest.mark.trio -async def test_single_insecure_security_transport_succeeds(): - transports_for_initiator = {"foo": InsecureTransport(initiator_key_pair)} - transports_for_noninitiator = {"foo": InsecureTransport(noninitiator_key_pair)} - +@pytest.mark.parametrize( + "security_protocol, transport_type", + ( + (PLAINTEXT_PROTOCOL_ID, InsecureSession), + (SECIO_PROTOCOL_ID, SecureSession), + (NOISE_PROTOCOL_ID, SecureSession), + ), +) +@pytest.mark.trio +async def test_single_insecure_security_transport_succeeds( + security_protocol, transport_type +): def assertion_func(conn): - assert isinstance(conn, InsecureSession) + assert isinstance(conn, transport_type) - await perform_simple_test( - assertion_func, transports_for_initiator, transports_for_noninitiator - ) + await perform_simple_test(assertion_func, security_protocol) @pytest.mark.trio async def test_default_insecure_security(): - transports_for_initiator = None - transports_for_noninitiator = None - - conn1 = None - conn2 = None - def assertion_func(conn): - nonlocal conn1 - nonlocal conn2 - if not conn1: - conn1 = conn - elif not conn2: - conn2 = conn - else: - assert conn1 == conn2 + assert isinstance(conn, InsecureSession) - await perform_simple_test( - assertion_func, transports_for_initiator, transports_for_noninitiator - ) + await perform_simple_test(assertion_func, None) diff --git a/tests/stream_muxer/conftest.py b/tests/stream_muxer/conftest.py index 248422d9..44606d59 100644 --- a/tests/stream_muxer/conftest.py +++ b/tests/stream_muxer/conftest.py @@ -4,14 +4,18 @@ from libp2p.tools.factories import mplex_conn_pair_factory, mplex_stream_pair_fa @pytest.fixture -async def mplex_conn_pair(is_host_secure): - async with mplex_conn_pair_factory(is_host_secure) as mplex_conn_pair: +async def mplex_conn_pair(security_protocol): + async with mplex_conn_pair_factory( + security_protocol=security_protocol + ) as mplex_conn_pair: assert mplex_conn_pair[0].is_initiator assert not mplex_conn_pair[1].is_initiator yield mplex_conn_pair[0], mplex_conn_pair[1] @pytest.fixture -async def mplex_stream_pair(is_host_secure): - async with mplex_stream_pair_factory(is_host_secure) as mplex_stream_pair: +async def mplex_stream_pair(security_protocol): + async with mplex_stream_pair_factory( + security_protocol=security_protocol + ) as mplex_stream_pair: yield mplex_stream_pair diff --git a/tests_interop/conftest.py b/tests_interop/conftest.py index 067db83f..b14f91cc 100644 --- a/tests_interop/conftest.py +++ b/tests_interop/conftest.py @@ -6,14 +6,15 @@ import pytest import trio from libp2p.io.abc import ReadWriteCloser +from libp2p.security.insecure.transport import PLAINTEXT_PROTOCOL_ID from libp2p.tools.factories import HostFactory, PubsubFactory from libp2p.tools.interop.daemon import make_p2pd from libp2p.tools.interop.utils import connect @pytest.fixture -def is_host_secure(): - return False +def security_protocol(): + return PLAINTEXT_PROTOCOL_ID @pytest.fixture @@ -38,7 +39,11 @@ def is_pubsub_signing_strict(): @pytest.fixture async def p2pds( - num_p2pds, is_host_secure, is_gossipsub, is_pubsub_signing, is_pubsub_signing_strict + num_p2pds, + security_protocol, + is_gossipsub, + is_pubsub_signing, + is_pubsub_signing_strict, ): async with AsyncExitStack() as stack: p2pds = [ @@ -46,7 +51,7 @@ async def p2pds( make_p2pd( get_unused_tcp_port(), get_unused_tcp_port(), - is_host_secure, + security_protocol, is_gossipsub=is_gossipsub, is_pubsub_signing=is_pubsub_signing, is_pubsub_signing_strict=is_pubsub_signing_strict, @@ -62,14 +67,16 @@ async def p2pds( @pytest.fixture -async def pubsubs(num_hosts, is_host_secure, is_gossipsub, is_pubsub_signing_strict): +async def pubsubs(num_hosts, security_protocol, is_gossipsub, is_pubsub_signing_strict): if is_gossipsub: yield PubsubFactory.create_batch_with_gossipsub( - num_hosts, is_secure=is_host_secure, strict_signing=is_pubsub_signing_strict + num_hosts, + security_protocol=security_protocol, + strict_signing=is_pubsub_signing_strict, ) else: yield PubsubFactory.create_batch_with_floodsub( - num_hosts, is_host_secure, strict_signing=is_pubsub_signing_strict + num_hosts, security_protocol, strict_signing=is_pubsub_signing_strict ) @@ -97,8 +104,10 @@ async def is_to_fail_daemon_stream(): @pytest.fixture -async def py_to_daemon_stream_pair(p2pds, is_host_secure, is_to_fail_daemon_stream): - async with HostFactory.create_batch_and_listen(is_host_secure, 1) as hosts: +async def py_to_daemon_stream_pair(p2pds, security_protocol, is_to_fail_daemon_stream): + async with HostFactory.create_batch_and_listen( + 1, security_protocol=security_protocol + ) as hosts: assert len(p2pds) >= 1 host = hosts[0] p2pd = p2pds[0] diff --git a/tests_interop/test_bindings.py b/tests_interop/test_bindings.py index 87cdbb1b..d7a169cb 100644 --- a/tests_interop/test_bindings.py +++ b/tests_interop/test_bindings.py @@ -6,8 +6,10 @@ from libp2p.tools.interop.utils import connect @pytest.mark.trio -async def test_connect(is_host_secure, p2pds): - async with HostFactory.create_batch_and_listen(is_host_secure, 1) as hosts: +async def test_connect(security_protocol, p2pds): + async with HostFactory.create_batch_and_listen( + 1, security_protocol=security_protocol + ) as hosts: p2pd = p2pds[0] host = hosts[0] assert len(await p2pd.control.list_peers()) == 0 diff --git a/tests_interop/test_echo.py b/tests_interop/test_echo.py index 85810a7f..eac8fd30 100644 --- a/tests_interop/test_echo.py +++ b/tests_interop/test_echo.py @@ -6,6 +6,7 @@ import pytest import trio from libp2p.peer.peerinfo import PeerInfo, info_from_p2p_addr +from libp2p.security.insecure.transport import PLAINTEXT_PROTOCOL_ID from libp2p.tools.factories import HostFactory from libp2p.tools.interop.envs import GO_BIN_PATH from libp2p.tools.interop.process import BaseInteractiveProcess @@ -20,10 +21,10 @@ class EchoProcess(BaseInteractiveProcess): _peer_info: PeerInfo def __init__( - self, port: int, is_secure: bool, destination: Multiaddr = None + self, port: int, security_protocol: TProtocol, destination: Multiaddr = None ) -> None: args = [f"-l={port}"] - if not is_secure: + if security_protocol == PLAINTEXT_PROTOCOL_ID: args.append("-insecure") if destination is not None: args.append(f"-d={str(destination)}") @@ -61,9 +62,11 @@ class EchoProcess(BaseInteractiveProcess): @pytest.mark.trio -async def test_insecure_conn_py_to_go(is_host_secure): - async with HostFactory.create_batch_and_listen(is_host_secure, 1) as hosts: - go_proc = EchoProcess(get_unused_tcp_port(), is_host_secure) +async def test_insecure_conn_py_to_go(security_protocol): + async with HostFactory.create_batch_and_listen( + 1, security_protocol=security_protocol + ) as hosts: + go_proc = EchoProcess(get_unused_tcp_port(), security_protocol) await go_proc.start() host = hosts[0] @@ -78,8 +81,10 @@ async def test_insecure_conn_py_to_go(is_host_secure): @pytest.mark.trio -async def test_insecure_conn_go_to_py(is_host_secure): - async with HostFactory.create_batch_and_listen(is_host_secure, 1) as hosts: +async def test_insecure_conn_go_to_py(security_protocol): + async with HostFactory.create_batch_and_listen( + 1, security_protocol=security_protocol + ) as hosts: host = hosts[0] expected_data = "Hello, world!\n" reply_data = "Replyooo!\n" @@ -94,6 +99,6 @@ async def test_insecure_conn_go_to_py(is_host_secure): host.set_stream_handler(ECHO_PROTOCOL_ID, _handle_echo) py_maddr = host.get_addrs()[0] - go_proc = EchoProcess(get_unused_tcp_port(), is_host_secure, py_maddr) + go_proc = EchoProcess(get_unused_tcp_port(), security_protocol, py_maddr) await go_proc.start() await event_handler_finished.wait() diff --git a/tests_interop/test_pubsub.py b/tests_interop/test_pubsub.py index 793f2446..cc7fa0dd 100644 --- a/tests_interop/test_pubsub.py +++ b/tests_interop/test_pubsub.py @@ -54,7 +54,7 @@ def validate_pubsub_msg(msg: rpc_pb2.Message, data: bytes, from_peer_id: ID) -> @pytest.mark.parametrize("num_p2pds", (2,)) @pytest.mark.trio async def test_pubsub( - p2pds, is_gossipsub, is_host_secure, is_pubsub_signing_strict, nursery + p2pds, is_gossipsub, security_protocol, is_pubsub_signing_strict, nursery ): pubsub_factory = None if is_gossipsub: @@ -63,7 +63,7 @@ async def test_pubsub( pubsub_factory = PubsubFactory.create_batch_with_floodsub async with pubsub_factory( - 1, is_secure=is_host_secure, strict_signing=is_pubsub_signing_strict + 1, security_protocol=security_protocol, strict_signing=is_pubsub_signing_strict ) as pubsubs: # # Test: Recognize pubsub peers on connection.