2020-02-19 23:15:03 +08:00
|
|
|
from typing import Any, AsyncIterator, Callable, Dict, List, Sequence, Tuple, cast
|
2019-08-29 21:38:06 +08:00
|
|
|
|
2019-12-24 14:44:28 +08:00
|
|
|
from async_exit_stack import AsyncExitStack
|
2019-12-11 09:07:21 +08:00
|
|
|
from async_generator import asynccontextmanager
|
2019-11-26 19:24:30 +08:00
|
|
|
from async_service import background_trio_service
|
2019-12-01 16:26:16 +08:00
|
|
|
import factory
|
2019-12-24 02:19:43 +08:00
|
|
|
from multiaddr import Multiaddr
|
2019-12-01 16:26:16 +08:00
|
|
|
import trio
|
2019-08-16 07:26:59 +08:00
|
|
|
|
2019-11-01 17:34:03 +08:00
|
|
|
from libp2p import generate_new_rsa_identity, generate_peer_id_from
|
2020-02-07 17:47:50 +08:00
|
|
|
from libp2p.crypto.ed25519 import create_new_key_pair as create_ed25519_key_pair
|
|
|
|
from libp2p.crypto.keys import KeyPair, PrivateKey
|
|
|
|
from libp2p.crypto.secp256k1 import create_new_key_pair as create_secp256k1_key_pair
|
2019-08-01 00:09:09 +08:00
|
|
|
from libp2p.host.basic_host import BasicHost
|
2019-12-06 17:06:37 +08:00
|
|
|
from libp2p.host.host_interface import IHost
|
2019-12-24 02:19:43 +08:00
|
|
|
from libp2p.host.routed_host import RoutedHost
|
2019-12-07 00:14:01 +08:00
|
|
|
from libp2p.io.abc import ReadWriteCloser
|
|
|
|
from libp2p.network.connection.raw_connection import RawConnection
|
|
|
|
from libp2p.network.connection.raw_connection_interface import IRawConnection
|
2019-09-17 23:38:11 +08:00
|
|
|
from libp2p.network.connection.swarm_connection import SwarmConn
|
2019-09-09 15:45:35 +08:00
|
|
|
from libp2p.network.stream.net_stream_interface import INetStream
|
2019-09-14 23:37:01 +08:00
|
|
|
from libp2p.network.swarm import Swarm
|
2019-12-03 17:27:49 +08:00
|
|
|
from libp2p.peer.id import ID
|
2019-12-24 02:19:43 +08:00
|
|
|
from libp2p.peer.peerinfo import PeerInfo
|
2019-12-06 17:06:37 +08:00
|
|
|
from libp2p.peer.peerstore import PeerStore
|
2019-12-17 18:17:28 +08:00
|
|
|
from libp2p.pubsub.abc import IPubsubRouter
|
2019-08-01 00:09:09 +08:00
|
|
|
from libp2p.pubsub.floodsub import FloodSub
|
|
|
|
from libp2p.pubsub.gossipsub import GossipSub
|
2020-02-28 03:57:00 +08:00
|
|
|
import libp2p.pubsub.pb.rpc_pb2 as rpc_pb2
|
|
|
|
from libp2p.pubsub.pubsub import Pubsub, get_peer_and_seqno_msg_id
|
2019-12-24 02:19:43 +08:00
|
|
|
from libp2p.routing.interfaces import IPeerRouting
|
2019-08-29 21:38:06 +08:00
|
|
|
from libp2p.security.insecure.transport import PLAINTEXT_PROTOCOL_ID, InsecureTransport
|
2020-02-16 00:42:49 +08:00
|
|
|
from libp2p.security.noise.messages import (
|
|
|
|
NoiseHandshakePayload,
|
|
|
|
make_handshake_payload_sig,
|
|
|
|
)
|
2020-02-19 23:15:03 +08:00
|
|
|
from libp2p.security.noise.transport import PROTOCOL_ID as NOISE_PROTOCOL_ID
|
2020-02-07 17:47:50 +08:00
|
|
|
from libp2p.security.noise.transport import Transport as NoiseTransport
|
2019-08-31 22:38:46 +08:00
|
|
|
import libp2p.security.secio.transport as secio
|
2020-02-07 17:47:50 +08:00
|
|
|
from libp2p.security.secure_conn_interface import ISecureConn
|
|
|
|
from libp2p.security.secure_transport_interface import ISecureTransport
|
2019-09-18 15:44:45 +08:00
|
|
|
from libp2p.stream_muxer.mplex.mplex import MPLEX_PROTOCOL_ID, Mplex
|
2019-09-18 21:51:09 +08:00
|
|
|
from libp2p.stream_muxer.mplex.mplex_stream import MplexStream
|
2019-12-06 17:06:37 +08:00
|
|
|
from libp2p.tools.constants import GOSSIPSUB_PARAMS
|
2019-11-01 17:34:03 +08:00
|
|
|
from libp2p.transport.tcp.tcp import TCP
|
2020-02-19 23:15:03 +08:00
|
|
|
from libp2p.transport.typing import TMuxerOptions, TSecurityOptions
|
2019-11-01 17:34:03 +08:00
|
|
|
from libp2p.transport.upgrader import TransportUpgrader
|
2019-08-29 21:38:06 +08:00
|
|
|
from libp2p.typing import TProtocol
|
2019-11-21 11:47:54 +08:00
|
|
|
|
2019-12-03 17:27:49 +08:00
|
|
|
from .constants import FLOODSUB_PROTOCOL_ID, GOSSIPSUB_PROTOCOL_ID, LISTEN_MADDR
|
2019-11-21 11:47:54 +08:00
|
|
|
from .utils import connect, connect_swarm
|
2019-08-01 00:09:09 +08:00
|
|
|
|
2020-02-19 23:15:03 +08:00
|
|
|
DEFAULT_SECURITY_PROTOCOL_ID = PLAINTEXT_PROTOCOL_ID
|
|
|
|
|
|
|
|
|
|
|
|
def default_key_pair_factory() -> KeyPair:
|
|
|
|
return generate_new_rsa_identity()
|
|
|
|
|
2019-08-01 00:09:09 +08:00
|
|
|
|
2019-12-03 17:27:49 +08:00
|
|
|
class IDFactory(factory.Factory):
|
|
|
|
class Meta:
|
|
|
|
model = ID
|
|
|
|
|
|
|
|
peer_id_bytes = factory.LazyFunction(
|
2020-02-19 23:15:03 +08:00
|
|
|
lambda: generate_peer_id_from(default_key_pair_factory())
|
2019-12-03 17:27:49 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
|
2019-11-25 17:32:53 +08:00
|
|
|
def initialize_peerstore_with_our_keypair(self_id: ID, key_pair: KeyPair) -> PeerStore:
|
2019-11-25 17:17:09 +08:00
|
|
|
peer_store = PeerStore()
|
2019-11-26 11:52:31 +08:00
|
|
|
peer_store.add_key_pair(self_id, key_pair)
|
2019-11-25 17:17:09 +08:00
|
|
|
return peer_store
|
|
|
|
|
|
|
|
|
2020-02-07 17:47:50 +08:00
|
|
|
def noise_static_key_factory() -> PrivateKey:
|
|
|
|
return create_ed25519_key_pair().private_key
|
|
|
|
|
|
|
|
|
2020-02-16 00:42:49 +08:00
|
|
|
def noise_handshake_payload_factory() -> NoiseHandshakePayload:
|
|
|
|
libp2p_keypair = create_secp256k1_key_pair()
|
|
|
|
noise_static_privkey = noise_static_key_factory()
|
|
|
|
return NoiseHandshakePayload(
|
|
|
|
libp2p_keypair.public_key,
|
|
|
|
make_handshake_payload_sig(
|
|
|
|
libp2p_keypair.private_key, noise_static_privkey.get_public_key()
|
|
|
|
),
|
|
|
|
)
|
|
|
|
|
|
|
|
|
2020-02-19 23:15:03 +08:00
|
|
|
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:
|
2020-02-09 00:33:26 +08:00
|
|
|
return NoiseTransport(
|
2020-02-19 23:15:03 +08:00
|
|
|
libp2p_keypair=key_pair,
|
2020-02-07 17:47:50 +08:00
|
|
|
noise_privkey=noise_static_key_factory(),
|
|
|
|
early_data=None,
|
|
|
|
with_noise_pipes=False,
|
|
|
|
)
|
|
|
|
|
|
|
|
|
2020-02-19 23:15:03 +08:00
|
|
|
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()
|
|
|
|
|
|
|
|
|
2019-12-07 00:14:01 +08:00
|
|
|
@asynccontextmanager
|
|
|
|
async def raw_conn_factory(
|
|
|
|
nursery: trio.Nursery
|
|
|
|
) -> AsyncIterator[Tuple[IRawConnection, IRawConnection]]:
|
|
|
|
conn_0 = None
|
|
|
|
conn_1 = None
|
2019-12-24 02:19:43 +08:00
|
|
|
event = trio.Event()
|
2019-12-07 00:14:01 +08:00
|
|
|
|
|
|
|
async def tcp_stream_handler(stream: ReadWriteCloser) -> None:
|
|
|
|
nonlocal conn_1
|
|
|
|
conn_1 = RawConnection(stream, initiator=False)
|
2019-12-24 02:19:43 +08:00
|
|
|
event.set()
|
2019-12-07 00:14:01 +08:00
|
|
|
await trio.sleep_forever()
|
|
|
|
|
|
|
|
tcp_transport = TCP()
|
|
|
|
listener = tcp_transport.create_listener(tcp_stream_handler)
|
|
|
|
await listener.listen(LISTEN_MADDR, nursery)
|
2019-12-17 15:50:55 +08:00
|
|
|
listening_maddr = listener.get_addrs()[0]
|
2019-12-07 00:14:01 +08:00
|
|
|
conn_0 = await tcp_transport.dial(listening_maddr)
|
2019-12-24 02:19:43 +08:00
|
|
|
await event.wait()
|
2019-12-07 00:14:01 +08:00
|
|
|
yield conn_0, conn_1
|
|
|
|
|
|
|
|
|
2020-02-07 17:47:50 +08:00
|
|
|
@asynccontextmanager
|
|
|
|
async def noise_conn_factory(
|
|
|
|
nursery: trio.Nursery
|
|
|
|
) -> AsyncIterator[Tuple[ISecureConn, ISecureConn]]:
|
2020-02-19 23:15:03 +08:00
|
|
|
local_transport = cast(
|
|
|
|
NoiseTransport, noise_transport_factory(create_secp256k1_key_pair())
|
|
|
|
)
|
|
|
|
remote_transport = cast(
|
|
|
|
NoiseTransport, noise_transport_factory(create_secp256k1_key_pair())
|
|
|
|
)
|
2020-02-07 17:47:50 +08:00
|
|
|
|
|
|
|
local_secure_conn: ISecureConn = None
|
|
|
|
remote_secure_conn: ISecureConn = None
|
|
|
|
|
|
|
|
async def upgrade_local_conn() -> None:
|
|
|
|
nonlocal local_secure_conn
|
|
|
|
local_secure_conn = await local_transport.secure_outbound(
|
2020-02-16 00:42:49 +08:00
|
|
|
local_conn, remote_transport.local_peer
|
2020-02-07 17:47:50 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
async def upgrade_remote_conn() -> None:
|
|
|
|
nonlocal remote_secure_conn
|
|
|
|
remote_secure_conn = await remote_transport.secure_inbound(remote_conn)
|
|
|
|
|
|
|
|
async with raw_conn_factory(nursery) as conns:
|
|
|
|
local_conn, remote_conn = conns
|
|
|
|
async with trio.open_nursery() as nursery:
|
|
|
|
nursery.start_soon(upgrade_local_conn)
|
|
|
|
nursery.start_soon(upgrade_remote_conn)
|
|
|
|
if local_secure_conn is None or remote_secure_conn is None:
|
|
|
|
raise Exception(
|
|
|
|
"local or remote secure conn has not been successfully upgraded"
|
|
|
|
f"local_secure_conn={local_secure_conn}, remote_secure_conn={remote_secure_conn}"
|
|
|
|
)
|
|
|
|
yield local_secure_conn, remote_secure_conn
|
|
|
|
|
|
|
|
|
2019-11-01 17:34:03 +08:00
|
|
|
class SwarmFactory(factory.Factory):
|
2019-09-14 23:37:01 +08:00
|
|
|
class Meta:
|
|
|
|
model = Swarm
|
|
|
|
|
2019-11-01 17:34:03 +08:00
|
|
|
class Params:
|
2020-02-19 23:15:03 +08:00
|
|
|
key_pair = factory.LazyFunction(default_key_pair_factory)
|
|
|
|
security_protocol = DEFAULT_SECURITY_PROTOCOL_ID
|
|
|
|
muxer_opt = factory.LazyFunction(default_muxer_transport_factory)
|
2019-11-01 17:34:03 +08:00
|
|
|
|
|
|
|
peer_id = factory.LazyAttribute(lambda o: generate_peer_id_from(o.key_pair))
|
2019-11-25 17:17:09 +08:00
|
|
|
peerstore = factory.LazyAttribute(
|
|
|
|
lambda o: initialize_peerstore_with_our_keypair(o.peer_id, o.key_pair)
|
|
|
|
)
|
2019-11-01 17:34:03 +08:00
|
|
|
upgrader = factory.LazyAttribute(
|
|
|
|
lambda o: TransportUpgrader(
|
2020-02-19 23:15:03 +08:00
|
|
|
(security_options_factory_factory(o.security_protocol))(o.key_pair),
|
|
|
|
o.muxer_opt,
|
2019-11-01 17:34:03 +08:00
|
|
|
)
|
|
|
|
)
|
|
|
|
transport = factory.LazyFunction(TCP)
|
|
|
|
|
2019-09-14 23:37:01 +08:00
|
|
|
@classmethod
|
2019-11-26 19:24:30 +08:00
|
|
|
@asynccontextmanager
|
2019-09-18 15:44:45 +08:00
|
|
|
async def create_and_listen(
|
2020-02-19 23:15:03 +08:00
|
|
|
cls,
|
|
|
|
key_pair: KeyPair = None,
|
|
|
|
security_protocol: TProtocol = None,
|
|
|
|
muxer_opt: TMuxerOptions = None,
|
2019-12-06 17:06:37 +08:00
|
|
|
) -> AsyncIterator[Swarm]:
|
2019-11-01 17:34:03 +08:00
|
|
|
# `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
|
|
|
|
# `factory.Factory.__init__`, in order to let the function initialize it.
|
2019-11-21 11:47:54 +08:00
|
|
|
optional_kwargs: Dict[str, Any] = {}
|
2019-11-01 17:34:03 +08:00
|
|
|
if key_pair is not None:
|
|
|
|
optional_kwargs["key_pair"] = key_pair
|
2020-02-19 23:15:03 +08:00
|
|
|
if security_protocol is not None:
|
|
|
|
optional_kwargs["security_protocol"] = security_protocol
|
2019-11-01 17:34:03 +08:00
|
|
|
if muxer_opt is not None:
|
|
|
|
optional_kwargs["muxer_opt"] = muxer_opt
|
2020-02-19 23:15:03 +08:00
|
|
|
swarm = cls(**optional_kwargs)
|
2019-11-26 19:24:30 +08:00
|
|
|
async with background_trio_service(swarm):
|
|
|
|
await swarm.listen(LISTEN_MADDR)
|
|
|
|
yield swarm
|
2019-09-14 23:37:01 +08:00
|
|
|
|
|
|
|
@classmethod
|
2019-11-26 19:24:30 +08:00
|
|
|
@asynccontextmanager
|
2019-09-14 23:37:01 +08:00
|
|
|
async def create_batch_and_listen(
|
2020-02-19 23:15:03 +08:00
|
|
|
cls,
|
|
|
|
number: int,
|
|
|
|
security_protocol: TProtocol = None,
|
|
|
|
muxer_opt: TMuxerOptions = None,
|
2019-12-06 17:06:37 +08:00
|
|
|
) -> AsyncIterator[Tuple[Swarm, ...]]:
|
2019-11-26 19:24:30 +08:00
|
|
|
async with AsyncExitStack() as stack:
|
|
|
|
ctx_mgrs = [
|
|
|
|
await stack.enter_async_context(
|
2020-02-19 23:15:03 +08:00
|
|
|
cls.create_and_listen(
|
|
|
|
security_protocol=security_protocol, muxer_opt=muxer_opt
|
|
|
|
)
|
2019-11-26 19:24:30 +08:00
|
|
|
)
|
2019-09-18 15:44:45 +08:00
|
|
|
for _ in range(number)
|
|
|
|
]
|
2019-12-06 17:06:37 +08:00
|
|
|
yield tuple(ctx_mgrs)
|
2019-08-01 00:09:09 +08:00
|
|
|
|
|
|
|
|
|
|
|
class HostFactory(factory.Factory):
|
|
|
|
class Meta:
|
|
|
|
model = BasicHost
|
|
|
|
|
2019-08-29 21:38:06 +08:00
|
|
|
class Params:
|
2020-02-19 23:15:03 +08:00
|
|
|
key_pair = factory.LazyFunction(default_key_pair_factory)
|
|
|
|
security_protocol: TProtocol = None
|
|
|
|
muxer_opt = factory.LazyFunction(default_muxer_transport_factory)
|
2019-08-29 21:38:06 +08:00
|
|
|
|
2020-02-19 23:15:03 +08:00
|
|
|
network = factory.LazyAttribute(
|
|
|
|
lambda o: SwarmFactory(
|
|
|
|
security_protocol=o.security_protocol, muxer_opt=o.muxer_opt
|
|
|
|
)
|
|
|
|
)
|
2019-12-24 02:19:43 +08:00
|
|
|
|
|
|
|
@classmethod
|
|
|
|
@asynccontextmanager
|
|
|
|
async def create_batch_and_listen(
|
2020-02-19 23:15:03 +08:00
|
|
|
cls,
|
|
|
|
number: int,
|
|
|
|
security_protocol: TProtocol = None,
|
|
|
|
muxer_opt: TMuxerOptions = None,
|
2019-12-24 02:19:43 +08:00
|
|
|
) -> AsyncIterator[Tuple[BasicHost, ...]]:
|
2020-02-19 23:15:03 +08:00
|
|
|
async with SwarmFactory.create_batch_and_listen(
|
|
|
|
number, security_protocol=security_protocol, muxer_opt=muxer_opt
|
|
|
|
) as swarms:
|
2019-12-24 02:19:43 +08:00
|
|
|
hosts = tuple(BasicHost(swarm) for swarm in swarms)
|
|
|
|
yield hosts
|
|
|
|
|
|
|
|
|
|
|
|
class DummyRouter(IPeerRouting):
|
|
|
|
_routing_table: Dict[ID, PeerInfo]
|
|
|
|
|
|
|
|
def __init__(self) -> None:
|
|
|
|
self._routing_table = dict()
|
|
|
|
|
|
|
|
def _add_peer(self, peer_id: ID, addrs: List[Multiaddr]) -> None:
|
|
|
|
self._routing_table[peer_id] = PeerInfo(peer_id, addrs)
|
|
|
|
|
|
|
|
async def find_peer(self, peer_id: ID) -> PeerInfo:
|
2021-02-23 23:02:34 +08:00
|
|
|
await trio.lowlevel.checkpoint()
|
2019-12-24 02:19:43 +08:00
|
|
|
return self._routing_table.get(peer_id, None)
|
|
|
|
|
|
|
|
|
|
|
|
class RoutedHostFactory(factory.Factory):
|
|
|
|
class Meta:
|
|
|
|
model = RoutedHost
|
|
|
|
|
|
|
|
class Params:
|
2020-02-19 23:15:03 +08:00
|
|
|
key_pair = factory.LazyFunction(default_key_pair_factory)
|
|
|
|
security_protocol: TProtocol = None
|
|
|
|
muxer_opt = factory.LazyFunction(default_muxer_transport_factory)
|
2019-12-24 02:19:43 +08:00
|
|
|
|
2019-11-01 17:34:03 +08:00
|
|
|
network = factory.LazyAttribute(
|
2020-02-19 23:15:03 +08:00
|
|
|
lambda o: HostFactory(
|
|
|
|
security_protocol=o.security_protocol, muxer_opt=o.muxer_opt
|
|
|
|
).get_network()
|
2019-11-01 17:34:03 +08:00
|
|
|
)
|
2019-12-24 02:19:43 +08:00
|
|
|
router = factory.LazyFunction(DummyRouter)
|
2019-09-15 14:59:59 +08:00
|
|
|
|
|
|
|
@classmethod
|
2019-11-26 19:24:30 +08:00
|
|
|
@asynccontextmanager
|
2019-09-15 14:59:59 +08:00
|
|
|
async def create_batch_and_listen(
|
2020-02-19 23:15:03 +08:00
|
|
|
cls,
|
|
|
|
number: int,
|
|
|
|
security_protocol: TProtocol = None,
|
|
|
|
muxer_opt: TMuxerOptions = None,
|
2019-12-24 02:19:43 +08:00
|
|
|
) -> AsyncIterator[Tuple[RoutedHost, ...]]:
|
|
|
|
routing_table = DummyRouter()
|
2020-02-19 23:15:03 +08:00
|
|
|
async with HostFactory.create_batch_and_listen(
|
|
|
|
number, security_protocol=security_protocol, muxer_opt=muxer_opt
|
|
|
|
) as hosts:
|
2019-12-24 02:19:43 +08:00
|
|
|
for host in hosts:
|
|
|
|
routing_table._add_peer(host.get_id(), host.get_addrs())
|
|
|
|
routed_hosts = tuple(
|
|
|
|
RoutedHost(host.get_network(), routing_table) for host in hosts
|
2019-11-26 19:24:30 +08:00
|
|
|
)
|
2019-12-24 02:19:43 +08:00
|
|
|
yield routed_hosts
|
2019-09-09 15:45:35 +08:00
|
|
|
|
2019-08-01 00:09:09 +08:00
|
|
|
|
|
|
|
class FloodsubFactory(factory.Factory):
|
|
|
|
class Meta:
|
|
|
|
model = FloodSub
|
|
|
|
|
|
|
|
protocols = (FLOODSUB_PROTOCOL_ID,)
|
|
|
|
|
|
|
|
|
|
|
|
class GossipsubFactory(factory.Factory):
|
|
|
|
class Meta:
|
|
|
|
model = GossipSub
|
|
|
|
|
|
|
|
protocols = (GOSSIPSUB_PROTOCOL_ID,)
|
|
|
|
degree = GOSSIPSUB_PARAMS.degree
|
|
|
|
degree_low = GOSSIPSUB_PARAMS.degree_low
|
|
|
|
degree_high = GOSSIPSUB_PARAMS.degree_high
|
|
|
|
time_to_live = GOSSIPSUB_PARAMS.time_to_live
|
|
|
|
gossip_window = GOSSIPSUB_PARAMS.gossip_window
|
2019-08-01 21:38:14 +08:00
|
|
|
gossip_history = GOSSIPSUB_PARAMS.gossip_history
|
2019-12-02 16:55:16 +08:00
|
|
|
heartbeat_initial_delay = GOSSIPSUB_PARAMS.heartbeat_initial_delay
|
2019-08-01 00:09:09 +08:00
|
|
|
heartbeat_interval = GOSSIPSUB_PARAMS.heartbeat_interval
|
|
|
|
|
|
|
|
|
|
|
|
class PubsubFactory(factory.Factory):
|
|
|
|
class Meta:
|
|
|
|
model = Pubsub
|
|
|
|
|
|
|
|
host = factory.SubFactory(HostFactory)
|
|
|
|
router = None
|
|
|
|
cache_size = None
|
2019-11-26 16:12:50 +08:00
|
|
|
strict_signing = False
|
2019-09-09 15:45:35 +08:00
|
|
|
|
2019-12-03 17:27:49 +08:00
|
|
|
@classmethod
|
|
|
|
@asynccontextmanager
|
2019-12-06 17:06:37 +08:00
|
|
|
async def create_and_start(
|
2020-02-28 03:57:00 +08:00
|
|
|
cls,
|
|
|
|
host: IHost,
|
|
|
|
router: IPubsubRouter,
|
|
|
|
cache_size: int,
|
|
|
|
strict_signing: bool,
|
|
|
|
msg_id_constructor: Callable[[rpc_pb2.Message], bytes] = None,
|
2019-12-06 17:06:37 +08:00
|
|
|
) -> AsyncIterator[Pubsub]:
|
2019-12-26 20:44:10 +08:00
|
|
|
pubsub = cls(
|
2019-12-24 02:19:43 +08:00
|
|
|
host=host,
|
|
|
|
router=router,
|
|
|
|
cache_size=cache_size,
|
|
|
|
strict_signing=strict_signing,
|
2020-02-28 03:57:00 +08:00
|
|
|
msg_id_constructor=msg_id_constructor,
|
2019-12-24 02:19:43 +08:00
|
|
|
)
|
2019-12-03 17:27:49 +08:00
|
|
|
async with background_trio_service(pubsub):
|
2020-01-27 00:10:33 +08:00
|
|
|
await pubsub.wait_until_ready()
|
2019-12-03 17:27:49 +08:00
|
|
|
yield pubsub
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
@asynccontextmanager
|
2019-12-06 17:06:37 +08:00
|
|
|
async def _create_batch_with_router(
|
|
|
|
cls,
|
|
|
|
number: int,
|
|
|
|
routers: Sequence[IPubsubRouter],
|
|
|
|
cache_size: int = None,
|
2019-12-24 02:19:43 +08:00
|
|
|
strict_signing: bool = False,
|
2020-02-19 23:15:03 +08:00
|
|
|
security_protocol: TProtocol = None,
|
|
|
|
muxer_opt: TMuxerOptions = None,
|
2020-02-28 03:57:00 +08:00
|
|
|
msg_id_constructor: Callable[[rpc_pb2.Message], bytes] = None,
|
2019-12-06 17:06:37 +08:00
|
|
|
) -> AsyncIterator[Tuple[Pubsub, ...]]:
|
2020-02-19 23:15:03 +08:00
|
|
|
async with HostFactory.create_batch_and_listen(
|
|
|
|
number, security_protocol=security_protocol, muxer_opt=muxer_opt
|
|
|
|
) as hosts:
|
2019-12-03 17:27:49 +08:00
|
|
|
# Pubsubs should exit before hosts
|
|
|
|
async with AsyncExitStack() as stack:
|
|
|
|
pubsubs = [
|
|
|
|
await stack.enter_async_context(
|
2020-02-28 03:57:00 +08:00
|
|
|
cls.create_and_start(
|
|
|
|
host, router, cache_size, strict_signing, msg_id_constructor
|
|
|
|
)
|
2019-12-03 17:27:49 +08:00
|
|
|
)
|
2019-12-06 17:06:37 +08:00
|
|
|
for host, router in zip(hosts, routers)
|
2019-12-03 17:27:49 +08:00
|
|
|
]
|
2019-12-06 17:06:37 +08:00
|
|
|
yield tuple(pubsubs)
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
@asynccontextmanager
|
|
|
|
async def create_batch_with_floodsub(
|
|
|
|
cls,
|
|
|
|
number: int,
|
|
|
|
cache_size: int = None,
|
2019-12-24 02:19:43 +08:00
|
|
|
strict_signing: bool = False,
|
2019-12-06 17:06:37 +08:00
|
|
|
protocols: Sequence[TProtocol] = None,
|
2020-02-19 23:15:03 +08:00
|
|
|
security_protocol: TProtocol = None,
|
|
|
|
muxer_opt: TMuxerOptions = None,
|
2020-02-28 03:57:00 +08:00
|
|
|
msg_id_constructor: Callable[
|
|
|
|
[rpc_pb2.Message], bytes
|
|
|
|
] = get_peer_and_seqno_msg_id,
|
2019-12-06 17:06:37 +08:00
|
|
|
) -> 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(
|
2020-02-19 23:15:03 +08:00
|
|
|
number,
|
|
|
|
floodsubs,
|
|
|
|
cache_size,
|
|
|
|
strict_signing,
|
|
|
|
security_protocol=security_protocol,
|
|
|
|
muxer_opt=muxer_opt,
|
2020-02-28 03:57:00 +08:00
|
|
|
msg_id_constructor=msg_id_constructor,
|
2019-12-06 17:06:37 +08:00
|
|
|
) as pubsubs:
|
|
|
|
yield pubsubs
|
2019-12-03 17:27:49 +08:00
|
|
|
|
2019-12-06 17:06:37 +08:00
|
|
|
@classmethod
|
|
|
|
@asynccontextmanager
|
|
|
|
async def create_batch_with_gossipsub(
|
|
|
|
cls,
|
|
|
|
number: int,
|
|
|
|
*,
|
|
|
|
cache_size: int = None,
|
2019-12-24 02:19:43 +08:00
|
|
|
strict_signing: bool = False,
|
2019-12-06 17:06:37 +08:00
|
|
|
protocols: Sequence[TProtocol] = None,
|
|
|
|
degree: int = GOSSIPSUB_PARAMS.degree,
|
|
|
|
degree_low: int = GOSSIPSUB_PARAMS.degree_low,
|
|
|
|
degree_high: int = GOSSIPSUB_PARAMS.degree_high,
|
|
|
|
time_to_live: int = GOSSIPSUB_PARAMS.time_to_live,
|
|
|
|
gossip_window: int = GOSSIPSUB_PARAMS.gossip_window,
|
|
|
|
gossip_history: int = GOSSIPSUB_PARAMS.gossip_history,
|
|
|
|
heartbeat_interval: float = GOSSIPSUB_PARAMS.heartbeat_interval,
|
2019-12-24 02:19:43 +08:00
|
|
|
heartbeat_initial_delay: float = GOSSIPSUB_PARAMS.heartbeat_initial_delay,
|
2020-02-19 23:15:03 +08:00
|
|
|
security_protocol: TProtocol = None,
|
|
|
|
muxer_opt: TMuxerOptions = None,
|
2020-02-28 03:57:00 +08:00
|
|
|
msg_id_constructor: Callable[
|
|
|
|
[rpc_pb2.Message], bytes
|
|
|
|
] = get_peer_and_seqno_msg_id,
|
2019-12-06 17:06:37 +08:00
|
|
|
) -> AsyncIterator[Tuple[Pubsub, ...]]:
|
|
|
|
if protocols is not None:
|
|
|
|
gossipsubs = GossipsubFactory.create_batch(
|
|
|
|
number,
|
|
|
|
protocols=protocols,
|
|
|
|
degree=degree,
|
|
|
|
degree_low=degree_low,
|
|
|
|
degree_high=degree_high,
|
|
|
|
time_to_live=time_to_live,
|
|
|
|
gossip_window=gossip_window,
|
|
|
|
heartbeat_interval=heartbeat_interval,
|
|
|
|
)
|
|
|
|
else:
|
|
|
|
gossipsubs = GossipsubFactory.create_batch(
|
|
|
|
number,
|
|
|
|
degree=degree,
|
|
|
|
degree_low=degree_low,
|
|
|
|
degree_high=degree_high,
|
|
|
|
time_to_live=time_to_live,
|
|
|
|
gossip_window=gossip_window,
|
|
|
|
heartbeat_interval=heartbeat_interval,
|
|
|
|
)
|
|
|
|
|
|
|
|
async with cls._create_batch_with_router(
|
2020-02-19 23:15:03 +08:00
|
|
|
number,
|
|
|
|
gossipsubs,
|
|
|
|
cache_size,
|
|
|
|
strict_signing,
|
|
|
|
security_protocol=security_protocol,
|
|
|
|
muxer_opt=muxer_opt,
|
2020-02-28 03:57:00 +08:00
|
|
|
msg_id_constructor=msg_id_constructor,
|
2019-12-06 17:06:37 +08:00
|
|
|
) as pubsubs:
|
|
|
|
async with AsyncExitStack() as stack:
|
|
|
|
for router in gossipsubs:
|
|
|
|
await stack.enter_async_context(background_trio_service(router))
|
|
|
|
yield pubsubs
|
2019-12-03 17:27:49 +08:00
|
|
|
|
2019-09-09 15:45:35 +08:00
|
|
|
|
2019-11-26 19:24:30 +08:00
|
|
|
@asynccontextmanager
|
2019-09-18 15:44:45 +08:00
|
|
|
async def swarm_pair_factory(
|
2020-02-19 23:15:03 +08:00
|
|
|
security_protocol: TProtocol = None, muxer_opt: TMuxerOptions = None
|
2019-12-06 17:06:37 +08:00
|
|
|
) -> AsyncIterator[Tuple[Swarm, Swarm]]:
|
2019-11-26 19:24:30 +08:00
|
|
|
async with SwarmFactory.create_batch_and_listen(
|
2020-02-19 23:15:03 +08:00
|
|
|
2, security_protocol=security_protocol, muxer_opt=muxer_opt
|
2019-11-26 19:24:30 +08:00
|
|
|
) as swarms:
|
|
|
|
await connect_swarm(swarms[0], swarms[1])
|
|
|
|
yield swarms[0], swarms[1]
|
2019-09-14 23:37:01 +08:00
|
|
|
|
|
|
|
|
2019-11-26 19:24:30 +08:00
|
|
|
@asynccontextmanager
|
2019-12-06 17:06:37 +08:00
|
|
|
async def host_pair_factory(
|
2020-02-19 23:15:03 +08:00
|
|
|
security_protocol: TProtocol = None, muxer_opt: TMuxerOptions = None
|
2019-12-06 17:06:37 +08:00
|
|
|
) -> AsyncIterator[Tuple[BasicHost, BasicHost]]:
|
2020-02-19 23:15:03 +08:00
|
|
|
async with HostFactory.create_batch_and_listen(
|
|
|
|
2, security_protocol=security_protocol, muxer_opt=muxer_opt
|
|
|
|
) as hosts:
|
2019-11-26 19:24:30 +08:00
|
|
|
await connect(hosts[0], hosts[1])
|
|
|
|
yield hosts[0], hosts[1]
|
2019-09-09 15:45:35 +08:00
|
|
|
|
|
|
|
|
2019-11-08 12:59:01 +08:00
|
|
|
@asynccontextmanager
|
2019-09-17 23:38:11 +08:00
|
|
|
async def swarm_conn_pair_factory(
|
2020-02-19 23:15:03 +08:00
|
|
|
security_protocol: TProtocol = None, muxer_opt: TMuxerOptions = None
|
2019-12-06 17:06:37 +08:00
|
|
|
) -> AsyncIterator[Tuple[SwarmConn, SwarmConn]]:
|
2020-02-19 23:15:03 +08:00
|
|
|
async with swarm_pair_factory(
|
|
|
|
security_protocol=security_protocol, muxer_opt=muxer_opt
|
|
|
|
) as swarms:
|
2019-11-26 19:24:30 +08:00
|
|
|
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)
|
2019-09-09 15:45:35 +08:00
|
|
|
|
|
|
|
|
2019-11-26 19:24:30 +08:00
|
|
|
@asynccontextmanager
|
2019-12-06 17:06:37 +08:00
|
|
|
async def mplex_conn_pair_factory(
|
2020-02-19 23:15:03 +08:00
|
|
|
security_protocol: TProtocol = None
|
2019-12-06 17:06:37 +08:00
|
|
|
) -> AsyncIterator[Tuple[Mplex, Mplex]]:
|
2020-02-19 23:15:03 +08:00
|
|
|
async with swarm_conn_pair_factory(
|
|
|
|
security_protocol=security_protocol, muxer_opt=default_muxer_transport_factory()
|
|
|
|
) as swarm_pair:
|
2019-11-26 19:24:30 +08:00
|
|
|
yield (
|
|
|
|
cast(Mplex, swarm_pair[0].muxed_conn),
|
|
|
|
cast(Mplex, swarm_pair[1].muxed_conn),
|
|
|
|
)
|
2019-09-18 15:44:45 +08:00
|
|
|
|
|
|
|
|
2019-11-26 19:24:30 +08:00
|
|
|
@asynccontextmanager
|
2019-12-06 17:06:37 +08:00
|
|
|
async def mplex_stream_pair_factory(
|
2020-02-19 23:15:03 +08:00
|
|
|
security_protocol: TProtocol = None
|
2019-12-06 17:06:37 +08:00
|
|
|
) -> AsyncIterator[Tuple[MplexStream, MplexStream]]:
|
2020-02-19 23:15:03 +08:00
|
|
|
async with mplex_conn_pair_factory(
|
|
|
|
security_protocol=security_protocol
|
|
|
|
) as mplex_conn_pair_info:
|
2019-11-26 19:24:30 +08:00
|
|
|
mplex_conn_0, mplex_conn_1 = mplex_conn_pair_info
|
2019-12-06 17:06:37 +08:00
|
|
|
stream_0 = cast(MplexStream, await mplex_conn_0.open_stream())
|
2019-11-26 19:24:30 +08:00
|
|
|
await trio.sleep(0.01)
|
|
|
|
stream_1: MplexStream
|
|
|
|
async with mplex_conn_1.streams_lock:
|
|
|
|
if len(mplex_conn_1.streams) != 1:
|
2019-11-29 19:09:56 +08:00
|
|
|
raise Exception("Mplex should not have any other stream")
|
2019-11-26 19:24:30 +08:00
|
|
|
stream_1 = tuple(mplex_conn_1.streams.values())[0]
|
2019-12-06 17:06:37 +08:00
|
|
|
yield stream_0, stream_1
|
2019-11-26 19:24:30 +08:00
|
|
|
|
|
|
|
|
|
|
|
@asynccontextmanager
|
2019-12-06 17:06:37 +08:00
|
|
|
async def net_stream_pair_factory(
|
2020-02-19 23:15:03 +08:00
|
|
|
security_protocol: TProtocol = None, muxer_opt: TMuxerOptions = None
|
2019-12-06 17:06:37 +08:00
|
|
|
) -> AsyncIterator[Tuple[INetStream, INetStream]]:
|
2019-11-21 11:47:54 +08:00
|
|
|
protocol_id = TProtocol("/example/id/1")
|
2019-09-09 15:45:35 +08:00
|
|
|
|
|
|
|
stream_1: INetStream
|
|
|
|
|
2020-02-04 17:09:26 +08:00
|
|
|
# Just a proxy, we only care about the stream.
|
|
|
|
# Add a barrier to avoid stream being removed.
|
|
|
|
event_handler_finished = trio.Event()
|
|
|
|
|
|
|
|
async def handler(stream: INetStream) -> None:
|
2019-09-09 15:45:35 +08:00
|
|
|
nonlocal stream_1
|
|
|
|
stream_1 = stream
|
2020-02-04 17:09:26 +08:00
|
|
|
await event_handler_finished.wait()
|
2019-09-09 15:45:35 +08:00
|
|
|
|
2020-02-19 23:15:03 +08:00
|
|
|
async with host_pair_factory(
|
|
|
|
security_protocol=security_protocol, muxer_opt=muxer_opt
|
|
|
|
) as hosts:
|
2019-11-26 19:24:30 +08:00
|
|
|
hosts[1].set_stream_handler(protocol_id, handler)
|
2019-09-09 15:45:35 +08:00
|
|
|
|
2019-11-26 19:24:30 +08:00
|
|
|
stream_0 = await hosts[0].new_stream(hosts[1].get_id(), [protocol_id])
|
|
|
|
yield stream_0, stream_1
|
2020-02-04 17:09:26 +08:00
|
|
|
event_handler_finished.set()
|