From d35b8ffc64347026392200ec2a0cc7ae9b24f1dc Mon Sep 17 00:00:00 2001 From: mhchia Date: Wed, 28 Aug 2019 21:43:34 +0800 Subject: [PATCH 01/14] Conform `stream_id` to go-mplex --- libp2p/peer/id.py | 2 +- libp2p/stream_muxer/abc.py | 9 +++-- libp2p/stream_muxer/mplex/mplex.py | 48 ++++++++++++----------- libp2p/stream_muxer/mplex/mplex_stream.py | 24 ++++++------ tests/pubsub/conftest.py | 18 +++++---- 5 files changed, 55 insertions(+), 46 deletions(-) diff --git a/libp2p/peer/id.py b/libp2p/peer/id.py index c1b52f0..f75563c 100644 --- a/libp2p/peer/id.py +++ b/libp2p/peer/id.py @@ -41,7 +41,7 @@ class ID: def __str__(self) -> str: if FRIENDLY_IDS: - return self.to_string()[2:8] + return f"" else: return self.to_string() diff --git a/libp2p/stream_muxer/abc.py b/libp2p/stream_muxer/abc.py index f16b805..0600dee 100644 --- a/libp2p/stream_muxer/abc.py +++ b/libp2p/stream_muxer/abc.py @@ -4,6 +4,7 @@ from typing import TYPE_CHECKING, Optional from libp2p.peer.id import ID from libp2p.security.secure_conn_interface import ISecureConn from libp2p.stream_muxer.mplex.constants import HeaderTags +from libp2p.stream_muxer.mplex.datastructures import StreamID if TYPE_CHECKING: # Prevent GenericProtocolHandlerFn introducing circular dependencies @@ -51,7 +52,7 @@ class IMuxedConn(ABC): """ @abstractmethod - async def read_buffer(self, stream_id: int) -> bytes: + async def read_buffer(self, stream_id: StreamID) -> bytes: """ Read a message from stream_id's buffer, check raw connection for new messages :param stream_id: stream id of stream to read from @@ -59,7 +60,7 @@ class IMuxedConn(ABC): """ @abstractmethod - async def read_buffer_nonblocking(self, stream_id: int) -> Optional[bytes]: + async def read_buffer_nonblocking(self, stream_id: StreamID) -> Optional[bytes]: """ Read a message from `stream_id`'s buffer, non-blockingly. """ @@ -78,7 +79,9 @@ class IMuxedConn(ABC): """ @abstractmethod - async def send_message(self, flag: HeaderTags, data: bytes, stream_id: int) -> int: + async def send_message( + self, flag: HeaderTags, data: bytes, stream_id: StreamID + ) -> int: """ sends a message over the connection :param header: header to use diff --git a/libp2p/stream_muxer/mplex/mplex.py b/libp2p/stream_muxer/mplex/mplex.py index 2dd2618..40fa67b 100644 --- a/libp2p/stream_muxer/mplex/mplex.py +++ b/libp2p/stream_muxer/mplex/mplex.py @@ -13,6 +13,7 @@ from libp2p.utils import ( ) from .constants import HeaderTags +from .datastructures import StreamID from .exceptions import StreamNotFound from .mplex_stream import MplexStream @@ -29,9 +30,9 @@ class Mplex(IMuxedConn): # TODO: `dataIn` in go implementation. Should be size of 8. # TODO: Also, `dataIn` is closed indicating EOF in Go. We don't have similar strategies # to let the `MplexStream`s know that EOF arrived (#235). - buffers: Dict[int, "asyncio.Queue[bytes]"] - stream_queue: "asyncio.Queue[int]" - next_stream_id: int + buffers: Dict[StreamID, "asyncio.Queue[bytes]"] + stream_queue: "asyncio.Queue[StreamID]" + next_channel_id: int # TODO: `generic_protocol_handler` should be refactored out of mplex conn. def __init__( @@ -49,10 +50,7 @@ class Mplex(IMuxedConn): """ self.secured_conn = secured_conn - if self.secured_conn.initiator: - self.next_stream_id = 0 - else: - self.next_stream_id = 1 + self.next_channel_id = 0 # Store generic protocol handler self.generic_protocol_handler = generic_protocol_handler @@ -85,7 +83,7 @@ class Mplex(IMuxedConn): """ raise NotImplementedError() - async def read_buffer(self, stream_id: int) -> bytes: + async def read_buffer(self, stream_id: StreamID) -> bytes: """ Read a message from buffer of the stream specified by `stream_id`, check secured connection for new messages. @@ -97,7 +95,7 @@ class Mplex(IMuxedConn): raise StreamNotFound(f"stream {stream_id} is not found") return await self.buffers[stream_id].get() - async def read_buffer_nonblocking(self, stream_id: int) -> Optional[bytes]: + async def read_buffer_nonblocking(self, stream_id: StreamID) -> Optional[bytes]: """ Read a message from buffer of the stream specified by `stream_id`, non-blockingly. `StreamNotFound` is raised when stream `stream_id` is not found in `Mplex`. @@ -108,13 +106,13 @@ class Mplex(IMuxedConn): return None return await self.buffers[stream_id].get() - def _get_next_stream_id(self) -> int: + def _get_next_channel_id(self) -> int: """ Get next available stream id :return: next available stream id for the connection """ - next_id = self.next_stream_id - self.next_stream_id += 2 + next_id = self.next_channel_id + self.next_channel_id += 1 return next_id async def open_stream(self) -> IMuxedStream: @@ -122,11 +120,12 @@ class Mplex(IMuxedConn): creates a new muxed_stream :return: a new ``MplexStream`` """ - stream_id = self._get_next_stream_id() - name = str(stream_id) - stream = MplexStream(name, stream_id, True, self) + channel_id = self._get_next_channel_id() + stream_id = StreamID(channel_id=channel_id, is_initiator=True) + name = str(channel_id) + stream = MplexStream(name, stream_id, self) self.buffers[stream_id] = asyncio.Queue() - # Default stream name is the `stream_id` + # Default stream name is the `channel_id` await self.send_message(HeaderTags.NewStream, name.encode(), stream_id) return stream @@ -135,10 +134,12 @@ class Mplex(IMuxedConn): accepts a muxed stream opened by the other end """ stream_id = await self.stream_queue.get() - stream = MplexStream(name, stream_id, False, self) + stream = MplexStream(name, stream_id, self) asyncio.ensure_future(self.generic_protocol_handler(stream)) - async def send_message(self, flag: HeaderTags, data: bytes, stream_id: int) -> int: + async def send_message( + self, flag: HeaderTags, data: bytes, stream_id: StreamID + ) -> int: """ sends a message over the connection :param header: header to use @@ -146,7 +147,7 @@ class Mplex(IMuxedConn): :param stream_id: stream the message is in """ # << by 3, then or with flag - header = (stream_id << 3) | flag.value + header = (stream_id.channel_id << 3) | flag.value header = encode_uvarint(header) if data is None: @@ -174,9 +175,10 @@ class Mplex(IMuxedConn): # TODO Deal with other types of messages using flag (currently _) while True: - stream_id, flag, message = await self.read_message() + channel_id, flag, message = await self.read_message() - if stream_id is not None and flag is not None and message is not None: + if channel_id is not None and flag is not None and message is not None: + stream_id = StreamID(channel_id=channel_id, is_initiator=bool(flag & 1)) if stream_id not in self.buffers: self.buffers[stream_id] = asyncio.Queue() await self.stream_queue.put(stream_id) @@ -214,6 +216,6 @@ class Mplex(IMuxedConn): return None, None, None flag = header & 0x07 - stream_id = header >> 3 + channel_id = header >> 3 - return stream_id, flag, message + return channel_id, flag, message diff --git a/libp2p/stream_muxer/mplex/mplex_stream.py b/libp2p/stream_muxer/mplex/mplex_stream.py index a6c2ce2..fe0261b 100644 --- a/libp2p/stream_muxer/mplex/mplex_stream.py +++ b/libp2p/stream_muxer/mplex/mplex_stream.py @@ -3,6 +3,7 @@ import asyncio from libp2p.stream_muxer.abc import IMuxedConn, IMuxedStream from .constants import HeaderTags +from .datastructures import StreamID class MplexStream(IMuxedStream): @@ -11,8 +12,7 @@ class MplexStream(IMuxedStream): """ name: str - stream_id: int - initiator: bool + stream_id: StreamID mplex_conn: IMuxedConn read_deadline: int write_deadline: int @@ -22,18 +22,14 @@ class MplexStream(IMuxedStream): _buf: bytearray - def __init__( - self, name: str, stream_id: int, initiator: bool, mplex_conn: IMuxedConn - ) -> None: + def __init__(self, name: str, stream_id: StreamID, mplex_conn: IMuxedConn) -> None: """ create new MuxedStream in muxer - :param stream_id: stream stream id - :param initiator: boolean if this is an initiator + :param stream_id: stream id of this stream :param mplex_conn: muxed connection of this muxed_stream """ self.name = name self.stream_id = stream_id - self.initiator = initiator self.mplex_conn = mplex_conn self.read_deadline = None self.write_deadline = None @@ -42,6 +38,10 @@ class MplexStream(IMuxedStream): self.stream_lock = asyncio.Lock() self._buf = bytearray() + @property + def is_initiator(self) -> bool: + return self.stream_id.is_initiator + async def read(self, n: int = -1) -> bytes: """ Read up to n bytes. Read possibly returns fewer than `n` bytes, @@ -85,7 +85,7 @@ class MplexStream(IMuxedStream): """ flag = ( HeaderTags.MessageInitiator - if self.initiator + if self.is_initiator else HeaderTags.MessageReceiver ) return await self.mplex_conn.send_message(flag, data, self.stream_id) @@ -98,7 +98,9 @@ class MplexStream(IMuxedStream): """ # TODO error handling with timeout # TODO understand better how mutexes are used from go repo - flag = HeaderTags.CloseInitiator if self.initiator else HeaderTags.CloseReceiver + flag = ( + HeaderTags.CloseInitiator if self.is_initiator else HeaderTags.CloseReceiver + ) await self.mplex_conn.send_message(flag, None, self.stream_id) remote_lock = False @@ -131,7 +133,7 @@ class MplexStream(IMuxedStream): if not self.remote_closed: flag = ( HeaderTags.ResetInitiator - if self.initiator + if self.is_initiator else HeaderTags.ResetReceiver ) await self.mplex_conn.send_message(flag, None, self.stream_id) diff --git a/tests/pubsub/conftest.py b/tests/pubsub/conftest.py index 82b7782..12faff3 100644 --- a/tests/pubsub/conftest.py +++ b/tests/pubsub/conftest.py @@ -23,14 +23,16 @@ async def hosts(num_hosts): await asyncio.gather( *[_host.get_network().listen(LISTEN_MADDR) for _host in _hosts] ) - yield _hosts - # Clean up - listeners = [] - for _host in _hosts: - for listener in _host.get_network().listeners.values(): - listener.server.close() - listeners.append(listener) - await asyncio.gather(*[listener.server.wait_closed() for listener in listeners]) + try: + yield _hosts + finally: + # Clean up + listeners = [] + for _host in _hosts: + for listener in _host.get_network().listeners.values(): + listener.server.close() + listeners.append(listener) + await asyncio.gather(*[listener.server.wait_closed() for listener in listeners]) @pytest.fixture From 34a4d7b0ede1457af6a9e2834f1a41de6bdfd88a Mon Sep 17 00:00:00 2001 From: mhchia Date: Wed, 28 Aug 2019 21:45:18 +0800 Subject: [PATCH 02/14] Add the missing StreamID class --- libp2p/stream_muxer/mplex/datastructures.py | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 libp2p/stream_muxer/mplex/datastructures.py diff --git a/libp2p/stream_muxer/mplex/datastructures.py b/libp2p/stream_muxer/mplex/datastructures.py new file mode 100644 index 0000000..28a4b41 --- /dev/null +++ b/libp2p/stream_muxer/mplex/datastructures.py @@ -0,0 +1,6 @@ +from typing import NamedTuple + + +class StreamID(NamedTuple): + channel_id: int + is_initiator: bool From b726d7c9da9919b88c8c9e91e95f82041513f4be Mon Sep 17 00:00:00 2001 From: mhchia Date: Wed, 28 Aug 2019 23:39:33 +0800 Subject: [PATCH 03/14] Add tox and CI for interop --- .travis.yml | 5 + install_interop_go_pkgs.sh | 13 ++ setup.py | 1 + tests/interop/__init__.py | 0 tests/interop/go_pkgs/README.md | 1 + tests/interop/go_pkgs/echo/main.go | 195 +++++++++++++++++ tests/interop/go_pkgs/go.mod | 11 + tests/interop/go_pkgs/go.sum | 323 +++++++++++++++++++++++++++++ tests/interop/test_echo.py | 93 +++++++++ tox.ini | 6 +- 10 files changed, 646 insertions(+), 2 deletions(-) create mode 100755 install_interop_go_pkgs.sh create mode 100644 tests/interop/__init__.py create mode 100644 tests/interop/go_pkgs/README.md create mode 100644 tests/interop/go_pkgs/echo/main.go create mode 100644 tests/interop/go_pkgs/go.mod create mode 100644 tests/interop/go_pkgs/go.sum create mode 100644 tests/interop/test_echo.py diff --git a/.travis.yml b/.travis.yml index 77b9e16..9dc8ce2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,6 +8,11 @@ matrix: - python: 3.7 dist: xenial env: TOXENV=lint + - python: 3.7 + dist: xenial + env: TOXENV=py37-interop + before_install: + - ./install_interop_go_pkgs.sh install: - pip install --upgrade pip diff --git a/install_interop_go_pkgs.sh b/install_interop_go_pkgs.sh new file mode 100755 index 0000000..e81d32e --- /dev/null +++ b/install_interop_go_pkgs.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +which go +if [ $? != 0 ]; then + wget https://dl.google.com/go/go1.12.6.linux-amd64.tar.gz + sudo tar -C /usr/local -xzf $GOPACKAGE + export GOPATH=$HOME/go + export GOROOT=/usr/local/go +fi + +go version +cd tests/interop/go_pkgs/ +go install ./... diff --git a/setup.py b/setup.py index 66d3d35..ff3dfa1 100644 --- a/setup.py +++ b/setup.py @@ -8,6 +8,7 @@ extras_require = { "factory-boy>=2.12.0,<3.0.0", "pytest>=4.6.3,<5.0.0", "pytest-asyncio>=0.10.0,<1.0.0", + "pexpect>=4.6,<5", ], "lint": [ "mypy>=0.701,<1.0", diff --git a/tests/interop/__init__.py b/tests/interop/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/interop/go_pkgs/README.md b/tests/interop/go_pkgs/README.md new file mode 100644 index 0000000..a3f2ba4 --- /dev/null +++ b/tests/interop/go_pkgs/README.md @@ -0,0 +1 @@ +Copied and modified from https://github.com/libp2p/go-libp2p-examples. diff --git a/tests/interop/go_pkgs/echo/main.go b/tests/interop/go_pkgs/echo/main.go new file mode 100644 index 0000000..ad958a3 --- /dev/null +++ b/tests/interop/go_pkgs/echo/main.go @@ -0,0 +1,195 @@ +package main + +import ( + "bufio" + "context" + "crypto/rand" + "flag" + "fmt" + "io" + "io/ioutil" + "log" + mrand "math/rand" + + "github.com/libp2p/go-libp2p" + "github.com/libp2p/go-libp2p-core/crypto" + "github.com/libp2p/go-libp2p-core/host" + "github.com/libp2p/go-libp2p-core/network" + "github.com/libp2p/go-libp2p-core/peer" + "github.com/libp2p/go-libp2p-core/peerstore" + + golog "github.com/ipfs/go-log" + ma "github.com/multiformats/go-multiaddr" + gologging "github.com/whyrusleeping/go-logging" +) + +// makeBasicHost creates a LibP2P host with a random peer ID listening on the +// given multiaddress. It won't encrypt the connection if insecure is true. +func makeBasicHost(listenPort int, insecure bool, randseed int64) (host.Host, error) { + + // If the seed is zero, use real cryptographic randomness. Otherwise, use a + // deterministic randomness source to make generated keys stay the same + // across multiple runs + var r io.Reader + if randseed == 0 { + r = rand.Reader + } else { + r = mrand.New(mrand.NewSource(randseed)) + } + + // Generate a key pair for this host. We will use it at least + // to obtain a valid host ID. + priv, _, err := crypto.GenerateKeyPairWithReader(crypto.RSA, 2048, r) + if err != nil { + return nil, err + } + + opts := []libp2p.Option{ + libp2p.ListenAddrStrings(fmt.Sprintf("/ip4/127.0.0.1/tcp/%d", listenPort)), + libp2p.Identity(priv), + libp2p.DisableRelay(), + } + + if insecure { + opts = append(opts, libp2p.NoSecurity) + } + + basicHost, err := libp2p.New(context.Background(), opts...) + if err != nil { + return nil, err + } + + // Build host multiaddress + hostAddr, _ := ma.NewMultiaddr(fmt.Sprintf("/ipfs/%s", basicHost.ID().Pretty())) + + // Now we can build a full multiaddress to reach this host + // by encapsulating both addresses: + addr := basicHost.Addrs()[0] + fullAddr := addr.Encapsulate(hostAddr) + log.Printf("I am %s\n", fullAddr) + if insecure { + log.Printf("Now run \"./echo -l %d -d %s -insecure\" on a different terminal\n", listenPort+1, fullAddr) + } else { + log.Printf("Now run \"./echo -l %d -d %s\" on a different terminal\n", listenPort+1, fullAddr) + } + + return basicHost, nil +} + +func main() { + // LibP2P code uses golog to log messages. They log with different + // string IDs (i.e. "swarm"). We can control the verbosity level for + // all loggers with: + golog.SetAllLoggers(gologging.DEBUG) // Change to DEBUG for extra info + + // Parse options from the command line + listenF := flag.Int("l", 0, "wait for incoming connections") + target := flag.String("d", "", "target peer to dial") + insecure := flag.Bool("insecure", false, "use an unencrypted connection") + seed := flag.Int64("seed", 0, "set random seed for id generation") + flag.Parse() + + if *listenF == 0 { + log.Fatal("Please provide a port to bind on with -l") + } + + // Make a host that listens on the given multiaddress + ha, err := makeBasicHost(*listenF, *insecure, *seed) + if err != nil { + log.Fatal(err) + } + + // Set a stream handler on host A. /echo/1.0.0 is + // a user-defined protocol name. + ha.SetStreamHandler("/echo/1.0.0", func(s network.Stream) { + log.Println("Got a new stream!") + if err := doEcho(s); err != nil { + log.Println(err) + s.Reset() + } else { + s.Close() + } + }) + + if *target == "" { + log.Println("listening for connections") + select {} // hang forever + } + /**** This is where the listener code ends ****/ + + // The following code extracts target's the peer ID from the + // given multiaddress + ipfsaddr, err := ma.NewMultiaddr(*target) + if err != nil { + log.Fatalln(err) + } + + pid, err := ipfsaddr.ValueForProtocol(ma.P_IPFS) + if err != nil { + log.Fatalln(err) + } + + peerid, err := peer.IDB58Decode(pid) + if err != nil { + log.Fatalln(err) + } + + // Decapsulate the /ipfs/ part from the target + // /ip4//ipfs/ becomes /ip4/ + targetPeerAddr, _ := ma.NewMultiaddr( + fmt.Sprintf("/ipfs/%s", peer.IDB58Encode(peerid))) + targetAddr := ipfsaddr.Decapsulate(targetPeerAddr) + log.Println("!@# targetAddr=", targetAddr) + + // We have a peer ID and a targetAddr so we add it to the peerstore + // so LibP2P knows how to contact it + ha.Peerstore().AddAddr(peerid, targetAddr, peerstore.PermanentAddrTTL) + + log.Println("!@# ipfsaddr=", ipfsaddr) + pinfo, err := peer.AddrInfoFromP2pAddr(ipfsaddr) + if err != nil { + log.Fatalf("failed to parse %v to pinfo\n", ipfsaddr) + } + + err = ha.Connect(context.Background(), *pinfo) + if err != nil { + panic(err) + } + + log.Println("connect with peer", *pinfo) + + // make a new stream from host B to host A + // it should be handled on host A by the handler we set above because + // we use the same /echo/1.0.0 protocol + s, err := ha.NewStream(context.Background(), peerid, "/echo/1.0.0") + if err != nil { + log.Fatalln(err) + } + + log.Println("opened stream") + + _, err = s.Write([]byte("Hello, world!\n")) + if err != nil { + log.Fatalln(err) + } + + out, err := ioutil.ReadAll(s) + if err != nil { + log.Fatalln(err) + } + + log.Printf("read reply: %q\n", out) +} + +// doEcho reads a line of data a stream and writes it back +func doEcho(s network.Stream) error { + buf := bufio.NewReader(s) + str, err := buf.ReadString('\n') + if err != nil { + return err + } + + log.Printf("read: %s\n", str) + _, err = s.Write([]byte(str)) + return err +} diff --git a/tests/interop/go_pkgs/go.mod b/tests/interop/go_pkgs/go.mod new file mode 100644 index 0000000..e3625e4 --- /dev/null +++ b/tests/interop/go_pkgs/go.mod @@ -0,0 +1,11 @@ +module interop + +go 1.12 + +require ( + github.com/ipfs/go-log v0.0.1 + github.com/libp2p/go-libp2p v0.3.1 + github.com/libp2p/go-libp2p-core v0.2.2 + github.com/multiformats/go-multiaddr v0.0.4 + github.com/whyrusleeping/go-logging v0.0.0-20170515211332-0457bb6b88fc +) diff --git a/tests/interop/go_pkgs/go.sum b/tests/interop/go_pkgs/go.sum new file mode 100644 index 0000000..ea85590 --- /dev/null +++ b/tests/interop/go_pkgs/go.sum @@ -0,0 +1,323 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/AndreasBriese/bbloom v0.0.0-20180913140656-343706a395b7/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= +github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/Kubuxu/go-os-helper v0.0.1/go.mod h1:N8B+I7vPCT80IcP58r50u4+gEEcsZETFUpAzWW2ep1Y= +github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/btcsuite/btcd v0.0.0-20190213025234-306aecffea32/go.mod h1:DrZx5ec/dmnfpw9KyYoQyYo7d0KEvTkk/5M/vbZjAr8= +github.com/btcsuite/btcd v0.0.0-20190523000118-16327141da8c h1:aEbSeNALREWXk0G7UdNhR3ayBV7tZ4M2PNmnrCAph6Q= +github.com/btcsuite/btcd v0.0.0-20190523000118-16327141da8c/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI= +github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= +github.com/btcsuite/btcutil v0.0.0-20190207003914-4c204d697803/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= +github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= +github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= +github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= +github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= +github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= +github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgraph-io/badger v1.5.5-0.20190226225317-8115aed38f8f/go.mod h1:VZxzAIRPHRVNRKRo6AXrX9BJegn6il06VMTZVJYCIjQ= +github.com/dgraph-io/badger v1.6.0-rc1/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4= +github.com/dgryski/go-farm v0.0.0-20190104051053-3adb47b1fb0f/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= +github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q= +github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gxed/hashland/keccakpg v0.0.1/go.mod h1:kRzw3HkwxFU1mpmPP8v1WyQzwdGfmKFJ6tItnhQ67kU= +github.com/gxed/hashland/murmur3 v0.0.1/go.mod h1:KjXop02n4/ckmZSnY2+HKcLud/tcmvhST0bie/0lS48= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/huin/goupnp v1.0.0 h1:wg75sLpL6DZqwHQN6E1Cfk6mtfzS45z8OV+ic+DtHRo= +github.com/huin/goupnp v1.0.0/go.mod h1:n9v9KO1tAxYH82qOn+UTIFQDmx5n1Zxd/ClZDMX7Bnc= +github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/ipfs/go-cid v0.0.1/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM= +github.com/ipfs/go-cid v0.0.2 h1:tuuKaZPU1M6HcejsO3AcYWW8sZ8MTvyxfc4uqB4eFE8= +github.com/ipfs/go-cid v0.0.2/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM= +github.com/ipfs/go-datastore v0.0.1/go.mod h1:d4KVXhMt913cLBEI/PXAy6ko+W7e9AhyAKBGh803qeE= +github.com/ipfs/go-detect-race v0.0.1/go.mod h1:8BNT7shDZPo99Q74BpGMK+4D8Mn4j46UU0LZ723meps= +github.com/ipfs/go-ds-badger v0.0.2/go.mod h1:Y3QpeSFWQf6MopLTiZD+VT6IC1yZqaGmjvRcKeSGij8= +github.com/ipfs/go-ds-badger v0.0.5/go.mod h1:g5AuuCGmr7efyzQhLL8MzwqcauPojGPUaHzfGTzuE3s= +github.com/ipfs/go-ds-leveldb v0.0.1/go.mod h1:feO8V3kubwsEF22n0YRQCffeb79OOYIykR4L04tMOYc= +github.com/ipfs/go-ipfs-delay v0.0.0-20181109222059-70721b86a9a8/go.mod h1:8SP1YXK1M1kXuc4KJZINY3TQQ03J2rwBG9QfXmbRPrw= +github.com/ipfs/go-ipfs-util v0.0.1 h1:Wz9bL2wB2YBJqggkA4dD7oSmqB4cAnpNbGrlHJulv50= +github.com/ipfs/go-ipfs-util v0.0.1/go.mod h1:spsl5z8KUnrve+73pOhSVZND1SIxPW5RyBCNzQxlJBc= +github.com/ipfs/go-log v0.0.1 h1:9XTUN/rW64BCG1YhPK9Hoy3q8nr4gOmHHBpgFdfw6Lc= +github.com/ipfs/go-log v0.0.1/go.mod h1:kL1d2/hzSpI0thNYjiKfjanbVNU+IIGA/WnNESY9leM= +github.com/jackpal/gateway v1.0.5 h1:qzXWUJfuMdlLMtt0a3Dgt+xkWQiA5itDEITVJtuSwMc= +github.com/jackpal/gateway v1.0.5/go.mod h1:lTpwd4ACLXmpyiCTRtfiNyVnUmqT9RivzCDQetPfnjA= +github.com/jackpal/go-nat-pmp v1.0.1 h1:i0LektDkO1QlrTm/cSuP+PyBCDnYvjPLGl4LdWEMiaA= +github.com/jackpal/go-nat-pmp v1.0.1/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= +github.com/jbenet/go-cienv v0.0.0-20150120210510-1bb1476777ec/go.mod h1:rGaEvXB4uRSZMmzKNLoXvTu1sfx+1kv/DojUlPrSZGs= +github.com/jbenet/go-cienv v0.1.0/go.mod h1:TqNnHUmJgXau0nCzC7kXWeotg3J9W34CUv5Djy1+FlA= +github.com/jbenet/go-temp-err-catcher v0.0.0-20150120210811-aac704a3f4f2 h1:vhC1OXXiT9R2pczegwz6moDvuRpggaroAXhPIseh57A= +github.com/jbenet/go-temp-err-catcher v0.0.0-20150120210811-aac704a3f4f2/go.mod h1:8GXXJV31xl8whumTzdZsTt3RnUIiPqzkyf7mxToRCMs= +github.com/jbenet/goprocess v0.0.0-20160826012719-b497e2f366b8/go.mod h1:Ly/wlsjFq/qrU3Rar62tu1gASgGw6chQbSh/XgIIXCY= +github.com/jbenet/goprocess v0.1.3 h1:YKyIEECS/XvcfHtBzxtjBBbWK+MbvA6dG8ASiqwvr10= +github.com/jbenet/goprocess v0.1.3/go.mod h1:5yspPrukOVuOLORacaBi858NqyClJPQxYZlqdZVfqY4= +github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= +github.com/kami-zh/go-capturer v0.0.0-20171211120116-e492ea43421d/go.mod h1:P2viExyCEfeWGU259JnaQ34Inuec4R38JCyBx2edgD0= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/koron/go-ssdp v0.0.0-20180514024734-4a0ed625a78b h1:wxtKgYHEncAU00muMD06dzLiahtGM1eouRNOzVV7tdQ= +github.com/koron/go-ssdp v0.0.0-20180514024734-4a0ed625a78b/go.mod h1:5Ky9EC2xfoUKUor0Hjgi2BJhCSXJfMOFlmyYrVKGQMk= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/libp2p/go-addr-util v0.0.1 h1:TpTQm9cXVRVSKsYbgQ7GKc3KbbHVTnbostgGaDEP+88= +github.com/libp2p/go-addr-util v0.0.1/go.mod h1:4ac6O7n9rIAKB1dnd+s8IbbMXkt+oBpzX4/+RACcnlQ= +github.com/libp2p/go-buffer-pool v0.0.1/go.mod h1:xtyIz9PMobb13WaxR6Zo1Pd1zXJKYg0a8KiIvDp3TzQ= +github.com/libp2p/go-buffer-pool v0.0.2 h1:QNK2iAFa8gjAe1SPz6mHSMuCcjs+X1wlHzeOSqcmlfs= +github.com/libp2p/go-buffer-pool v0.0.2/go.mod h1:MvaB6xw5vOrDl8rYZGLFdKAuk/hRoRZd1Vi32+RXyFM= +github.com/libp2p/go-conn-security-multistream v0.1.0 h1:aqGmto+ttL/uJgX0JtQI0tD21CIEy5eYd1Hlp0juHY0= +github.com/libp2p/go-conn-security-multistream v0.1.0/go.mod h1:aw6eD7LOsHEX7+2hJkDxw1MteijaVcI+/eP2/x3J1xc= +github.com/libp2p/go-eventbus v0.0.2/go.mod h1:Hr/yGlwxA/stuLnpMiu82lpNKpvRy3EaJxPu40XYOwk= +github.com/libp2p/go-eventbus v0.1.0 h1:mlawomSAjjkk97QnYiEmHsLu7E136+2oCWSHRUvMfzQ= +github.com/libp2p/go-eventbus v0.1.0/go.mod h1:vROgu5cs5T7cv7POWlWxBaVLxfSegC5UGQf8A2eEmx4= +github.com/libp2p/go-flow-metrics v0.0.1 h1:0gxuFd2GuK7IIP5pKljLwps6TvcuYgvG7Atqi3INF5s= +github.com/libp2p/go-flow-metrics v0.0.1/go.mod h1:Iv1GH0sG8DtYN3SVJ2eG221wMiNpZxBdp967ls1g+k8= +github.com/libp2p/go-libp2p v0.3.1 h1:opd8/1Sm9zFG37LzNQsIzMTMeBabhlcX5VlvLrNZPV0= +github.com/libp2p/go-libp2p v0.3.1/go.mod h1:e6bwxbdYH1HqWTz8faTChKGR0BjPc8p+6SyP8GTTR7Y= +github.com/libp2p/go-libp2p-autonat v0.1.0 h1:aCWAu43Ri4nU0ZPO7NyLzUvvfqd0nE3dX0R/ZGYVgOU= +github.com/libp2p/go-libp2p-autonat v0.1.0/go.mod h1:1tLf2yXxiE/oKGtDwPYWTSYG3PtvYlJmg7NeVtPRqH8= +github.com/libp2p/go-libp2p-blankhost v0.1.1/go.mod h1:pf2fvdLJPsC1FsVrNP3DUUvMzUts2dsLLBEpo1vW1ro= +github.com/libp2p/go-libp2p-blankhost v0.1.3/go.mod h1:KML1//wiKR8vuuJO0y3LUd1uLv+tlkGTAr3jC0S5cLg= +github.com/libp2p/go-libp2p-circuit v0.1.1 h1:eopfG9fAg6rEHWQO1TSrLosXDgYbbbu/RTva/tBANus= +github.com/libp2p/go-libp2p-circuit v0.1.1/go.mod h1:Ahq4cY3V9VJcHcn1SBXjr78AbFkZeIRmfunbA7pmFh8= +github.com/libp2p/go-libp2p-core v0.0.1/go.mod h1:g/VxnTZ/1ygHxH3dKok7Vno1VfpvGcGip57wjTU4fco= +github.com/libp2p/go-libp2p-core v0.0.4/go.mod h1:jyuCQP356gzfCFtRKyvAbNkyeuxb7OlyhWZ3nls5d2I= +github.com/libp2p/go-libp2p-core v0.0.6/go.mod h1:0d9xmaYAVY5qmbp/fcgxHT3ZJsLjYeYPMJAUKpaCHrE= +github.com/libp2p/go-libp2p-core v0.2.0/go.mod h1:X0eyB0Gy93v0DZtSYbEM7RnMChm9Uv3j7yRXjO77xSI= +github.com/libp2p/go-libp2p-core v0.2.2 h1:Sv1ggdoMx9c7v7FOFkR7agraHCnAgqYsXrU1ARSRUMs= +github.com/libp2p/go-libp2p-core v0.2.2/go.mod h1:8fcwTbsG2B+lTgRJ1ICZtiM5GWCWZVoVrLaDRvIRng0= +github.com/libp2p/go-libp2p-crypto v0.1.0/go.mod h1:sPUokVISZiy+nNuTTH/TY+leRSxnFj/2GLjtOTW90hI= +github.com/libp2p/go-libp2p-discovery v0.1.0 h1:j+R6cokKcGbnZLf4kcNwpx6mDEUPF3N6SrqMymQhmvs= +github.com/libp2p/go-libp2p-discovery v0.1.0/go.mod h1:4F/x+aldVHjHDHuX85x1zWoFTGElt8HnoDzwkFZm29g= +github.com/libp2p/go-libp2p-loggables v0.1.0 h1:h3w8QFfCt2UJl/0/NW4K829HX/0S4KD31PQ7m8UXXO8= +github.com/libp2p/go-libp2p-loggables v0.1.0/go.mod h1:EyumB2Y6PrYjr55Q3/tiJ/o3xoDasoRYM7nOzEpoa90= +github.com/libp2p/go-libp2p-mplex v0.2.0/go.mod h1:Ejl9IyjvXJ0T9iqUTE1jpYATQ9NM3g+OtR+EMMODbKo= +github.com/libp2p/go-libp2p-mplex v0.2.1 h1:E1xaJBQnbSiTHGI1gaBKmKhu1TUKkErKJnE8iGvirYI= +github.com/libp2p/go-libp2p-mplex v0.2.1/go.mod h1:SC99Rxs8Vuzrf/6WhmH41kNn13TiYdAWNYHrwImKLnE= +github.com/libp2p/go-libp2p-nat v0.0.4 h1:+KXK324yaY701On8a0aGjTnw8467kW3ExKcqW2wwmyw= +github.com/libp2p/go-libp2p-nat v0.0.4/go.mod h1:N9Js/zVtAXqaeT99cXgTV9e75KpnWCvVOiGzlcHmBbY= +github.com/libp2p/go-libp2p-netutil v0.1.0/go.mod h1:3Qv/aDqtMLTUyQeundkKsA+YCThNdbQD54k3TqjpbFU= +github.com/libp2p/go-libp2p-peer v0.2.0/go.mod h1:RCffaCvUyW2CJmG2gAWVqwePwW7JMgxjsHm7+J5kjWY= +github.com/libp2p/go-libp2p-peerstore v0.1.0/go.mod h1:2CeHkQsr8svp4fZ+Oi9ykN1HBb6u0MOvdJ7YIsmcwtY= +github.com/libp2p/go-libp2p-peerstore v0.1.3 h1:wMgajt1uM2tMiqf4M+4qWKVyyFc8SfA+84VV9glZq1M= +github.com/libp2p/go-libp2p-peerstore v0.1.3/go.mod h1:BJ9sHlm59/80oSkpWgr1MyY1ciXAXV397W6h1GH/uKI= +github.com/libp2p/go-libp2p-secio v0.1.0/go.mod h1:tMJo2w7h3+wN4pgU2LSYeiKPrfqBgkOsdiKK77hE7c8= +github.com/libp2p/go-libp2p-secio v0.2.0 h1:ywzZBsWEEz2KNTn5RtzauEDq5RFEefPsttXYwAWqHng= +github.com/libp2p/go-libp2p-secio v0.2.0/go.mod h1:2JdZepB8J5V9mBp79BmwsaPQhRPNN2NrnB2lKQcdy6g= +github.com/libp2p/go-libp2p-swarm v0.1.0/go.mod h1:wQVsCdjsuZoc730CgOvh5ox6K8evllckjebkdiY5ta4= +github.com/libp2p/go-libp2p-swarm v0.2.1 h1:9A8oQqPIZvbaRyrjViHeDYS7fE7fNtP7BRWdJrBHbe8= +github.com/libp2p/go-libp2p-swarm v0.2.1/go.mod h1:x07b4zkMFo2EvgPV2bMTlNmdQc8i+74Jjio7xGvsTgU= +github.com/libp2p/go-libp2p-testing v0.0.2/go.mod h1:gvchhf3FQOtBdr+eFUABet5a4MBLK8jM3V4Zghvmi+E= +github.com/libp2p/go-libp2p-testing v0.0.3/go.mod h1:gvchhf3FQOtBdr+eFUABet5a4MBLK8jM3V4Zghvmi+E= +github.com/libp2p/go-libp2p-testing v0.0.4/go.mod h1:gvchhf3FQOtBdr+eFUABet5a4MBLK8jM3V4Zghvmi+E= +github.com/libp2p/go-libp2p-testing v0.1.0/go.mod h1:xaZWMJrPUM5GlDBxCeGUi7kI4eqnjVyavGroI2nxEM0= +github.com/libp2p/go-libp2p-transport-upgrader v0.1.1 h1:PZMS9lhjK9VytzMCW3tWHAXtKXmlURSc3ZdvwEcKCzw= +github.com/libp2p/go-libp2p-transport-upgrader v0.1.1/go.mod h1:IEtA6or8JUbsV07qPW4r01GnTenLW4oi3lOPbUMGJJA= +github.com/libp2p/go-libp2p-yamux v0.2.0/go.mod h1:Db2gU+XfLpm6E4rG5uGCFX6uXA8MEXOxFcRoXUODaK8= +github.com/libp2p/go-libp2p-yamux v0.2.1 h1:Q3XYNiKCC2vIxrvUJL+Jg1kiyeEaIDNKLjgEjo3VQdI= +github.com/libp2p/go-libp2p-yamux v0.2.1/go.mod h1:1FBXiHDk1VyRM1C0aez2bCfHQ4vMZKkAQzZbkSQt5fI= +github.com/libp2p/go-maddr-filter v0.0.4/go.mod h1:6eT12kSQMA9x2pvFQa+xesMKUBlj9VImZbj3B9FBH/Q= +github.com/libp2p/go-maddr-filter v0.0.5 h1:CW3AgbMO6vUvT4kf87y4N+0P8KUl2aqLYhrGyDUbLSg= +github.com/libp2p/go-maddr-filter v0.0.5/go.mod h1:Jk+36PMfIqCJhAnaASRH83bdAvfDRp/w6ENFaC9bG+M= +github.com/libp2p/go-mplex v0.0.3/go.mod h1:pK5yMLmOoBR1pNCqDlA2GQrdAVTMkqFalaTWe7l4Yd0= +github.com/libp2p/go-mplex v0.1.0 h1:/nBTy5+1yRyY82YaO6HXQRnO5IAGsXTjEJaR3LdTPc0= +github.com/libp2p/go-mplex v0.1.0/go.mod h1:SXgmdki2kwCUlCCbfGLEgHjC4pFqhTp0ZoV6aiKgxDU= +github.com/libp2p/go-msgio v0.0.2/go.mod h1:63lBBgOTDKQL6EWazRMCwXsEeEeK9O2Cd+0+6OOuipQ= +github.com/libp2p/go-msgio v0.0.4 h1:agEFehY3zWJFUHK6SEMR7UYmk2z6kC3oeCM7ybLhguA= +github.com/libp2p/go-msgio v0.0.4/go.mod h1:63lBBgOTDKQL6EWazRMCwXsEeEeK9O2Cd+0+6OOuipQ= +github.com/libp2p/go-nat v0.0.3 h1:l6fKV+p0Xa354EqQOQP+d8CivdLM4kl5GxC1hSc/UeI= +github.com/libp2p/go-nat v0.0.3/go.mod h1:88nUEt0k0JD45Bk93NIwDqjlhiOwOoV36GchpcVc1yI= +github.com/libp2p/go-openssl v0.0.2/go.mod h1:v8Zw2ijCSWBQi8Pq5GAixw6DbFfa9u6VIYDXnvOXkc0= +github.com/libp2p/go-reuseport v0.0.1 h1:7PhkfH73VXfPJYKQ6JwS5I/eVcoyYi9IMNGc6FWpFLw= +github.com/libp2p/go-reuseport v0.0.1/go.mod h1:jn6RmB1ufnQwl0Q1f+YxAj8isJgDCQzaaxIFYDhcYEA= +github.com/libp2p/go-reuseport-transport v0.0.2 h1:WglMwyXyBu61CMkjCCtnmqNqnjib0GIEjMiHTwR/KN4= +github.com/libp2p/go-reuseport-transport v0.0.2/go.mod h1:YkbSDrvjUVDL6b8XqriyA20obEtsW9BLkuOUyQAOCbs= +github.com/libp2p/go-stream-muxer v0.0.1/go.mod h1:bAo8x7YkSpadMTbtTaxGVHWUQsR/l5MEaHbKaliuT14= +github.com/libp2p/go-stream-muxer-multistream v0.2.0 h1:714bRJ4Zy9mdhyTLJ+ZKiROmAFwUHpeRidG+q7LTQOg= +github.com/libp2p/go-stream-muxer-multistream v0.2.0/go.mod h1:j9eyPol/LLRqT+GPLSxvimPhNph4sfYfMoDPd7HkzIc= +github.com/libp2p/go-tcp-transport v0.1.0 h1:IGhowvEqyMFknOar4FWCKSWE0zL36UFKQtiRQD60/8o= +github.com/libp2p/go-tcp-transport v0.1.0/go.mod h1:oJ8I5VXryj493DEJ7OsBieu8fcg2nHGctwtInJVpipc= +github.com/libp2p/go-ws-transport v0.1.0 h1:F+0OvvdmPTDsVc4AjPHjV7L7Pk1B7D5QwtDcKE2oag4= +github.com/libp2p/go-ws-transport v0.1.0/go.mod h1:rjw1MG1LU9YDC6gzmwObkPd/Sqwhw7yT74kj3raBFuo= +github.com/libp2p/go-yamux v1.2.2/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow= +github.com/libp2p/go-yamux v1.2.3 h1:xX8A36vpXb59frIzWFdEgptLMsOANMFq2K7fPRlunYI= +github.com/libp2p/go-yamux v1.2.3/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.1 h1:G1f5SKeVxmagw/IyvzvtZE4Gybcc4Tr1tf7I8z0XgOg= +github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= +github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.5 h1:tHXDdz1cpzGaovsTB+TVB8q90WEokoVmfMqoVcrLUgw= +github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= +github.com/miekg/dns v1.1.12/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 h1:lYpkrQH5ajf0OXOcUbGjvZxxijuBwbbmlSxLiuofa+g= +github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ= +github.com/minio/sha256-simd v0.0.0-20190131020904-2d45a736cd16/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U= +github.com/minio/sha256-simd v0.0.0-20190328051042-05b4dd3047e5/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U= +github.com/minio/sha256-simd v0.1.0 h1:U41/2erhAKcmSI14xh/ZTUdBPOzDOIfS93ibzUSl8KM= +github.com/minio/sha256-simd v0.1.0/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8= +github.com/mr-tron/base58 v1.1.1/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8= +github.com/mr-tron/base58 v1.1.2 h1:ZEw4I2EgPKDJ2iEw0cNmLB3ROrEmkOtXIkaG7wZg+78= +github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= +github.com/multiformats/go-base32 v0.0.3 h1:tw5+NhuwaOjJCC5Pp82QuXbrmLzWg7uxlMFp8Nq/kkI= +github.com/multiformats/go-base32 v0.0.3/go.mod h1:pLiuGC8y0QR3Ue4Zug5UzK9LjgbkL8NSQj0zQ5Nz/AA= +github.com/multiformats/go-multiaddr v0.0.1/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44= +github.com/multiformats/go-multiaddr v0.0.2/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44= +github.com/multiformats/go-multiaddr v0.0.4 h1:WgMSI84/eRLdbptXMkMWDXPjPq7SPLIgGUVm2eroyU4= +github.com/multiformats/go-multiaddr v0.0.4/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44= +github.com/multiformats/go-multiaddr-dns v0.0.1/go.mod h1:9kWcqw/Pj6FwxAwW38n/9403szc57zJPs45fmnznu3Q= +github.com/multiformats/go-multiaddr-dns v0.0.2 h1:/Bbsgsy3R6e3jf2qBahzNHzww6usYaZ0NhNH3sqdFS8= +github.com/multiformats/go-multiaddr-dns v0.0.2/go.mod h1:9kWcqw/Pj6FwxAwW38n/9403szc57zJPs45fmnznu3Q= +github.com/multiformats/go-multiaddr-fmt v0.0.1 h1:5YjeOIzbX8OTKVaN72aOzGIYW7PnrZrnkDyOfAWRSMA= +github.com/multiformats/go-multiaddr-fmt v0.0.1/go.mod h1:aBYjqL4T/7j4Qx+R73XSv/8JsgnRFlf0w2KGLCmXl3Q= +github.com/multiformats/go-multiaddr-net v0.0.1 h1:76O59E3FavvHqNg7jvzWzsPSW5JSi/ek0E4eiDVbg9g= +github.com/multiformats/go-multiaddr-net v0.0.1/go.mod h1:nw6HSxNmCIQH27XPGBuX+d1tnvM7ihcFwHMSstNAVUU= +github.com/multiformats/go-multibase v0.0.1 h1:PN9/v21eLywrFWdFNsFKaU04kLJzuYzmrJR+ubhT9qA= +github.com/multiformats/go-multibase v0.0.1/go.mod h1:bja2MqRZ3ggyXtZSEDKpl0uO/gviWFaSteVbWT51qgs= +github.com/multiformats/go-multihash v0.0.1/go.mod h1:w/5tugSrLEbWqlcgJabL3oHFKTwfvkofsjW2Qa1ct4U= +github.com/multiformats/go-multihash v0.0.5 h1:1wxmCvTXAifAepIMyF39vZinRw5sbqjPs/UIi93+uik= +github.com/multiformats/go-multihash v0.0.5/go.mod h1:lt/HCbqlQwlPBz7lv0sQCdtfcMtlJvakRUn/0Ual8po= +github.com/multiformats/go-multistream v0.1.0 h1:UpO6jrsjqs46mqAK3n6wKRYFhugss9ArzbyUzU+4wkQ= +github.com/multiformats/go-multistream v0.1.0/go.mod h1:fJTiDfXJVmItycydCnNx4+wSzZ5NwG2FEVAI30fiovg= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/opentracing/opentracing-go v1.0.2 h1:3jA2P6O1F9UOrWVpwrIo17pu01KWvNWg4X946/Y5Zwg= +github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/smola/gocompat v0.2.0/go.mod h1:1B0MlxbmoZNo3h8guHp8HztB3BSYR5itql9qtVc0ypY= +github.com/spacemonkeygo/openssl v0.0.0-20181017203307-c2dcc5cca94a/go.mod h1:7AyxJNCJ7SBZ1MfVQCWD6Uqo2oubI2Eq2y2eqf+A5r0= +github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572/go.mod h1:w0SWMsp6j9O/dk4/ZpIhL+3CkG8ofA2vuv7k+ltqUMc= +github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= +github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +github.com/src-d/envconfig v1.0.0/go.mod h1:Q9YQZ7BKITldTBnoxsE5gOeB5y66RyPXeue/R4aaNBc= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= +github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1/go.mod h1:8UvriyWtv5Q5EOgjHaSseUEdkQfvwFv1I/In/O2M9gc= +github.com/whyrusleeping/go-logging v0.0.0-20170515211332-0457bb6b88fc h1:9lDbC6Rz4bwmou+oE6Dt4Cb2BGMur5eR/GYptkKUVHo= +github.com/whyrusleeping/go-logging v0.0.0-20170515211332-0457bb6b88fc/go.mod h1:bopw91TMyo8J3tvftk8xmU2kPmlrt4nScJQZU2hE5EM= +github.com/whyrusleeping/go-notifier v0.0.0-20170827234753-097c5d47330f h1:M/lL30eFZTKnomXY6huvM6G0+gVquFNf6mxghaWlFUg= +github.com/whyrusleeping/go-notifier v0.0.0-20170827234753-097c5d47330f/go.mod h1:cZNvX9cFybI01GriPRMXDtczuvUhgbcYr9iCGaNlRv8= +github.com/whyrusleeping/mafmt v1.2.8 h1:TCghSl5kkwEE0j+sU/gudyhVMRlpBin8fMBBHg59EbA= +github.com/whyrusleeping/mafmt v1.2.8/go.mod h1:faQJFPbLSxzD9xpA02ttW/tS9vZykNvXwGvqIpk20FA= +github.com/whyrusleeping/mdns v0.0.0-20180901202407-ef14215e6b30/go.mod h1:j4l84WPFclQPj320J9gp0XwNKBb3U0zt5CBqjPp22G4= +github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7 h1:E9S12nwJwEOXe2d6gT6qxdvqMnNq+VnSsKPgm2ZZNds= +github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7/go.mod h1:X2c0RVCI1eSUFI8eLcY3c0423ykwiUdxLJtkDvruhjI= +github.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7VPsoEPHyzalCE06qoARUCeBBE= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +go.opencensus.io v0.21.0 h1:mU6zScU4U1YAFPHEHYk+3JC4SY7JxgkqS10ZOSyksNg= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190225124518-7f87c0fbb88b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190618222545-ea8f1a30c443 h1:IcSOAf4PyMp3U3XbIEj1/xJ2BjNN2jWv7JoyOsMxXUU= +golang.org/x/crypto v0.0.0-20190618222545-ea8f1a30c443/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190227160552-c95aed5357e7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190219092855-153ac476189d/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb h1:fgwFCsaw9buMuxNd6+DQfAuSFqbNiQZpcgJQAgJsK6k= +golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181130052023-1c3d964395ce/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/src-d/go-cli.v0 v0.0.0-20181105080154-d492247bbc0d/go.mod h1:z+K8VcOYVYcSwSjGebuDL6176A1XskgbtNl64NSg+n8= +gopkg.in/src-d/go-log.v1 v1.0.1/go.mod h1:GN34hKP0g305ysm2/hctJ0Y8nWP3zxXXJ8GFabTyABE= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/tests/interop/test_echo.py b/tests/interop/test_echo.py new file mode 100644 index 0000000..a187cf8 --- /dev/null +++ b/tests/interop/test_echo.py @@ -0,0 +1,93 @@ +import asyncio +import os +import pathlib +import sys + +from multiaddr import Multiaddr +import pexpect +import pytest + +from libp2p import generate_new_rsa_identity, new_node +from libp2p.peer.peerinfo import info_from_p2p_addr +from libp2p.security.insecure.transport import PLAINTEXT_PROTOCOL_ID, InsecureTransport +from libp2p.typing import TProtocol +from tests.configs import LISTEN_MADDR + + +GOPATH = pathlib.Path(os.environ["GOPATH"]) +ECHO_PATH = GOPATH / "bin" / "echo" +ECHO_PROTOCOL_ID = TProtocol("/echo/1.0.0") +NEW_LINE = "\r\n" + + +@pytest.mark.asyncio +async def test_insecure_conn_py_to_go(unused_tcp_port): + try: + go_proc = pexpect.spawn( + str(ECHO_PATH), + [f"-l={unused_tcp_port}", "-insecure"], + logfile=sys.stdout, + encoding="utf-8", + ) + await go_proc.expect(r"I am ([\w\./]+)" + NEW_LINE, async_=True) + maddr_str = go_proc.match.group(1) + maddr_str = maddr_str.replace("ipfs", "p2p") + maddr = Multiaddr(maddr_str) + go_pinfo = info_from_p2p_addr(maddr) + await go_proc.expect("listening for connections", async_=True) + + key_pair = generate_new_rsa_identity() + insecure_tpt = InsecureTransport(key_pair) + host = await new_node( + key_pair=key_pair, sec_opt={PLAINTEXT_PROTOCOL_ID: insecure_tpt} + ) + await host.connect(go_pinfo) + await go_proc.expect("swarm listener accepted connection", async_=True) + s = await host.new_stream(go_pinfo.peer_id, [ECHO_PROTOCOL_ID]) + + await go_proc.expect("Got a new stream!", async_=True) + data = "data321123\n" + await s.write(data.encode()) + await go_proc.expect(f"read: {data[:-1]}", async_=True) + echoed_resp = await s.read(len(data)) + assert echoed_resp.decode() == data + await s.close() + finally: + go_proc.close() + + +@pytest.mark.asyncio +async def test_insecure_conn_go_to_py(unused_tcp_port): + key_pair = generate_new_rsa_identity() + insecure_tpt = InsecureTransport(key_pair) + host = await new_node( + key_pair=key_pair, sec_opt={PLAINTEXT_PROTOCOL_ID: insecure_tpt} + ) + await host.get_network().listen(LISTEN_MADDR) + expected_data = "Hello, world!\n" + reply_data = "Replyooo!\n" + event_handler_finished = asyncio.Event() + + async def _handle_echo(stream): + read_data = await stream.read(len(expected_data)) + assert read_data == expected_data.encode() + event_handler_finished.set() + await stream.write(reply_data.encode()) + await stream.close() + + host.set_stream_handler(ECHO_PROTOCOL_ID, _handle_echo) + py_maddr = host.get_addrs()[0] + go_proc = pexpect.spawn( + str(ECHO_PATH), + [f"-l={unused_tcp_port}", "-insecure", f"-d={str(py_maddr)}"], + logfile=sys.stdout, + encoding="utf-8", + ) + try: + await go_proc.expect(r"I am ([\w\./]+)" + NEW_LINE, async_=True) + await go_proc.expect("connect with peer", async_=True) + await go_proc.expect("opened stream", async_=True) + await event_handler_finished.wait() + await go_proc.expect(f"read reply: .*{reply_data.rstrip()}.*", async_=True) + finally: + go_proc.close() diff --git a/tox.ini b/tox.ini index 2eef74f..0d85779 100644 --- a/tox.ini +++ b/tox.ini @@ -3,6 +3,7 @@ [tox] envlist = py37-test + py37-interop lint [flake8] @@ -26,10 +27,11 @@ skip_glob= [testenv] deps = -passenv = CI TRAVIS TRAVIS_* +passenv = CI TRAVIS TRAVIS_* GOPATH extras = test commands = - pytest tests/ + test: py.test --ignore=tests/interop + interop: py.test tests/interop basepython = py37: python3.7 From 15f62dff68f38d1e461ed8d450d12a90b7be8df9 Mon Sep 17 00:00:00 2001 From: mhchia Date: Wed, 28 Aug 2019 23:48:25 +0800 Subject: [PATCH 04/14] Use go12 in CI --- .travis.yml | 6 ++++++ install_interop_go_pkgs.sh | 8 -------- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9dc8ce2..59ee019 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,7 +11,13 @@ matrix: - python: 3.7 dist: xenial env: TOXENV=py37-interop + sudo: true before_install: + - wget https://dl.google.com/go/go1.12.6.linux-amd64.tar.gz + - sudo tar -C /usr/local -xzf $GOPACKAGE + - export GOPATH=$HOME/go + - export GOROOT=/usr/local/go + - export PATH=$GOROOT/bin:$GOPATH/bin:$PATH - ./install_interop_go_pkgs.sh install: diff --git a/install_interop_go_pkgs.sh b/install_interop_go_pkgs.sh index e81d32e..cdf3193 100755 --- a/install_interop_go_pkgs.sh +++ b/install_interop_go_pkgs.sh @@ -1,13 +1,5 @@ #!/bin/bash -which go -if [ $? != 0 ]; then - wget https://dl.google.com/go/go1.12.6.linux-amd64.tar.gz - sudo tar -C /usr/local -xzf $GOPACKAGE - export GOPATH=$HOME/go - export GOROOT=/usr/local/go -fi - go version cd tests/interop/go_pkgs/ go install ./... From e0399beed8372938baa2a6be53703bebbbe406df Mon Sep 17 00:00:00 2001 From: mhchia Date: Wed, 28 Aug 2019 23:50:46 +0800 Subject: [PATCH 05/14] Fix tar argument --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 59ee019..2373658 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,7 +14,7 @@ matrix: sudo: true before_install: - wget https://dl.google.com/go/go1.12.6.linux-amd64.tar.gz - - sudo tar -C /usr/local -xzf $GOPACKAGE + - sudo tar -C /usr/local -xzf go1.12.6.linux-amd64.tar.gz - export GOPATH=$HOME/go - export GOROOT=/usr/local/go - export PATH=$GOROOT/bin:$GOPATH/bin:$PATH From 64c0dab3af98985c9301ead838f4508fa0bfc4aa Mon Sep 17 00:00:00 2001 From: mhchia Date: Thu, 29 Aug 2019 00:01:48 +0800 Subject: [PATCH 06/14] Fix isort --- tests/interop/test_echo.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/interop/test_echo.py b/tests/interop/test_echo.py index a187cf8..57f7bd3 100644 --- a/tests/interop/test_echo.py +++ b/tests/interop/test_echo.py @@ -13,7 +13,6 @@ from libp2p.security.insecure.transport import PLAINTEXT_PROTOCOL_ID, InsecureTr from libp2p.typing import TProtocol from tests.configs import LISTEN_MADDR - GOPATH = pathlib.Path(os.environ["GOPATH"]) ECHO_PATH = GOPATH / "bin" / "echo" ECHO_PROTOCOL_ID = TProtocol("/echo/1.0.0") From c61a06706ac89285d697fe35786a978a56fbe24d Mon Sep 17 00:00:00 2001 From: mhchia Date: Thu, 29 Aug 2019 21:38:06 +0800 Subject: [PATCH 07/14] Refactor interop tests and factories - Add `close` and `disconnect` in `Host` - Add `close` and `close_peer` in `Network` - Change `IListener.close` to async, to await for server's closing - Add factories for security transports, and modify `HostFactory` --- libp2p/host/basic_host.py | 6 + libp2p/host/host_interface.py | 8 ++ libp2p/network/network_interface.py | 8 ++ libp2p/network/swarm.py | 22 +++- libp2p/stream_muxer/mplex/mplex.py | 13 +- libp2p/transport/listener_interface.py | 3 +- libp2p/transport/tcp/tcp.py | 10 +- tests/conftest.py | 33 +++++ tests/{pubsub => }/factories.py | 37 ++++-- tests/interop/test_echo.py | 118 +++++++++--------- tests/pubsub/conftest.py | 33 +---- tests/pubsub/dummy_account_node.py | 2 +- .../floodsub_integration_test_settings.py | 2 +- tests/pubsub/test_floodsub.py | 2 +- .../test_gossipsub_backward_compatibility.py | 3 +- 15 files changed, 184 insertions(+), 116 deletions(-) create mode 100644 tests/conftest.py rename tests/{pubsub => }/factories.py (51%) diff --git a/libp2p/host/basic_host.py b/libp2p/host/basic_host.py index cdee9a0..e6fc515 100644 --- a/libp2p/host/basic_host.py +++ b/libp2p/host/basic_host.py @@ -107,3 +107,9 @@ class BasicHost(IHost): return await self._network.dial_peer(peer_info.peer_id) + + async def disconnect(self, peer_id: ID) -> None: + await self._network.close_peer(peer_id) + + async def close(self) -> None: + await self._network.close() diff --git a/libp2p/host/host_interface.py b/libp2p/host/host_interface.py index bcaefad..6b1ef03 100644 --- a/libp2p/host/host_interface.py +++ b/libp2p/host/host_interface.py @@ -71,3 +71,11 @@ class IHost(ABC): :param peer_info: peer_info of the host we want to connect to :type peer_info: peer.peerinfo.PeerInfo """ + + @abstractmethod + async def disconnect(self, peer_id: ID) -> None: + pass + + @abstractmethod + async def close(self) -> None: + pass diff --git a/libp2p/network/network_interface.py b/libp2p/network/network_interface.py index d9cdf48..9ed2d16 100644 --- a/libp2p/network/network_interface.py +++ b/libp2p/network/network_interface.py @@ -70,3 +70,11 @@ class INetwork(ABC): :param notifee: object implementing Notifee interface :return: true if notifee registered successfully, false otherwise """ + + @abstractmethod + async def close(self) -> None: + pass + + @abstractmethod + async def close_peer(self, peer_id: ID) -> None: + pass diff --git a/libp2p/network/swarm.py b/libp2p/network/swarm.py index 52df727..d0f2fe8 100644 --- a/libp2p/network/swarm.py +++ b/libp2p/network/swarm.py @@ -264,12 +264,24 @@ class Swarm(INetwork): def add_router(self, router: IPeerRouting) -> None: self.router = router - # TODO: `tear_down` - async def tear_down(self) -> None: - # Reference: https://github.com/libp2p/go-libp2p-swarm/blob/8be680aef8dea0a4497283f2f98470c2aeae6b65/swarm.go#L118 # noqa: E501 - pass + async def close(self) -> None: + # TODO: Prevent from new listeners and conns being added. + # Reference: https://github.com/libp2p/go-libp2p-swarm/blob/8be680aef8dea0a4497283f2f98470c2aeae6b65/swarm.go#L124-L134 # noqa: E501 - # TODO: `disconnect`? + # Close listeners + await asyncio.gather( + *[listener.close() for listener in self.listeners.values()] + ) + + # Close connections + await asyncio.gather( + *[connection.close() for connection in self.connections.values()] + ) + + async def close_peer(self, peer_id: ID) -> None: + connection = self.connections[peer_id] + del self.connections[peer_id] + await connection.close() def create_generic_protocol_handler(swarm: Swarm) -> GenericProtocolHandlerFn: diff --git a/libp2p/stream_muxer/mplex/mplex.py b/libp2p/stream_muxer/mplex/mplex.py index 40fa67b..5f55a66 100644 --- a/libp2p/stream_muxer/mplex/mplex.py +++ b/libp2p/stream_muxer/mplex/mplex.py @@ -1,5 +1,6 @@ import asyncio -from typing import Dict, Optional, Tuple +from typing import Any # noqa: F401 +from typing import Dict, List, Optional, Tuple from libp2p.network.typing import GenericProtocolHandlerFn from libp2p.peer.id import ID @@ -34,6 +35,8 @@ class Mplex(IMuxedConn): stream_queue: "asyncio.Queue[StreamID]" next_channel_id: int + _tasks: List["asyncio.Future[Any]"] + # TODO: `generic_protocol_handler` should be refactored out of mplex conn. def __init__( self, @@ -63,8 +66,10 @@ class Mplex(IMuxedConn): self.stream_queue = asyncio.Queue() + self._tasks = [] + # Kick off reading - asyncio.ensure_future(self.handle_incoming()) + self._tasks.append(asyncio.ensure_future(self.handle_incoming())) @property def initiator(self) -> bool: @@ -74,6 +79,8 @@ class Mplex(IMuxedConn): """ close the stream muxer and underlying secured connection """ + for task in self._tasks: + task.cancel() await self.secured_conn.close() def is_closed(self) -> bool: @@ -135,7 +142,7 @@ class Mplex(IMuxedConn): """ stream_id = await self.stream_queue.get() stream = MplexStream(name, stream_id, self) - asyncio.ensure_future(self.generic_protocol_handler(stream)) + self._tasks.append(asyncio.ensure_future(self.generic_protocol_handler(stream))) async def send_message( self, flag: HeaderTags, data: bytes, stream_id: StreamID diff --git a/libp2p/transport/listener_interface.py b/libp2p/transport/listener_interface.py index fecc3b9..9664f06 100644 --- a/libp2p/transport/listener_interface.py +++ b/libp2p/transport/listener_interface.py @@ -21,9 +21,8 @@ class IListener(ABC): """ @abstractmethod - def close(self) -> bool: + async def close(self) -> None: """ close the listener such that no more connections can be open on this transport instance - :return: return True if successful """ diff --git a/libp2p/transport/tcp/tcp.py b/libp2p/transport/tcp/tcp.py index 8e29f9b..49c7d9b 100644 --- a/libp2p/transport/tcp/tcp.py +++ b/libp2p/transport/tcp/tcp.py @@ -45,20 +45,16 @@ class TCPListener(IListener): # TODO check if server is listening return self.multiaddrs - def close(self) -> bool: + async def close(self) -> None: """ close the listener such that no more connections can be open on this transport instance - :return: return True if successful """ if self.server is None: - return False + return self.server.close() - _loop = asyncio.get_event_loop() - _loop.run_until_complete(self.server.wait_closed()) - _loop.close() + await self.server.wait_closed() self.server = None - return True class TCP(ITransport): diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..9101fa6 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,33 @@ +import asyncio + +import pytest + +from .configs import LISTEN_MADDR +from .factories import HostFactory + + +@pytest.fixture +def is_host_secure(): + return False + + +@pytest.fixture +def num_hosts(): + return 3 + + +@pytest.fixture +async def hosts(num_hosts, is_host_secure): + _hosts = HostFactory.create_batch(num_hosts, is_secure=is_host_secure) + await asyncio.gather( + *[_host.get_network().listen(LISTEN_MADDR) for _host in _hosts] + ) + try: + yield _hosts + finally: + # TODO: It's possible that `close` raises exceptions currently, + # due to the connection reset things. Though we are not so careful about that when + # cleaning up the tasks, it is probably better to handle the exceptions properly. + await asyncio.gather( + *[_host.close() for _host in _hosts], return_exceptions=True + ) diff --git a/tests/pubsub/factories.py b/tests/factories.py similarity index 51% rename from tests/pubsub/factories.py rename to tests/factories.py index b57c29b..0604094 100644 --- a/tests/pubsub/factories.py +++ b/tests/factories.py @@ -1,12 +1,17 @@ +from typing import Dict + import factory -from libp2p import initialize_default_swarm -from libp2p.crypto.rsa import create_new_key_pair +from libp2p import generate_new_rsa_identity, initialize_default_swarm +from libp2p.crypto.keys import KeyPair from libp2p.host.basic_host import BasicHost from libp2p.pubsub.floodsub import FloodSub from libp2p.pubsub.gossipsub import GossipSub from libp2p.pubsub.pubsub import Pubsub -from tests.configs import LISTEN_MADDR +from libp2p.security.base_transport import BaseSecureTransport +from libp2p.security.insecure.transport import PLAINTEXT_PROTOCOL_ID, InsecureTransport +from libp2p.security.secio.transport import ID, Transport +from libp2p.typing import TProtocol from tests.pubsub.configs import ( FLOODSUB_PROTOCOL_ID, GOSSIPSUB_PARAMS, @@ -14,16 +19,34 @@ from tests.pubsub.configs import ( ) -def swarm_factory(): - private_key = create_new_key_pair() - return initialize_default_swarm(private_key, transport_opt=[str(LISTEN_MADDR)]) +def security_transport_factory( + is_secure: bool, key_pair: KeyPair +) -> Dict[TProtocol, BaseSecureTransport]: + protocol_id: TProtocol + security_transport: BaseSecureTransport + if not is_secure: + protocol_id = PLAINTEXT_PROTOCOL_ID + security_transport = InsecureTransport(key_pair) + else: + protocol_id = ID + security_transport = Transport(key_pair) + return {protocol_id: security_transport} + + +def swarm_factory(is_secure: bool): + key_pair = generate_new_rsa_identity() + sec_opt = security_transport_factory(is_secure, key_pair) + return initialize_default_swarm(key_pair, sec_opt=sec_opt) class HostFactory(factory.Factory): class Meta: model = BasicHost - network = factory.LazyFunction(swarm_factory) + class Params: + is_secure = False + + network = factory.LazyAttribute(lambda o: swarm_factory(o.is_secure)) class FloodsubFactory(factory.Factory): diff --git a/tests/interop/test_echo.py b/tests/interop/test_echo.py index 57f7bd3..513ea86 100644 --- a/tests/interop/test_echo.py +++ b/tests/interop/test_echo.py @@ -7,11 +7,8 @@ from multiaddr import Multiaddr import pexpect import pytest -from libp2p import generate_new_rsa_identity, new_node from libp2p.peer.peerinfo import info_from_p2p_addr -from libp2p.security.insecure.transport import PLAINTEXT_PROTOCOL_ID, InsecureTransport from libp2p.typing import TProtocol -from tests.configs import LISTEN_MADDR GOPATH = pathlib.Path(os.environ["GOPATH"]) ECHO_PATH = GOPATH / "bin" / "echo" @@ -19,50 +16,68 @@ ECHO_PROTOCOL_ID = TProtocol("/echo/1.0.0") NEW_LINE = "\r\n" -@pytest.mark.asyncio -async def test_insecure_conn_py_to_go(unused_tcp_port): +@pytest.fixture +def proc_factory(): + procs = [] + + def call_proc(cmd, args, logfile=None, encoding=None): + if logfile is None: + logfile = sys.stdout + if encoding is None: + encoding = "utf-8" + proc = pexpect.spawn(cmd, args, logfile=logfile, encoding=encoding) + procs.append(proc) + return proc + try: - go_proc = pexpect.spawn( - str(ECHO_PATH), - [f"-l={unused_tcp_port}", "-insecure"], - logfile=sys.stdout, - encoding="utf-8", - ) - await go_proc.expect(r"I am ([\w\./]+)" + NEW_LINE, async_=True) - maddr_str = go_proc.match.group(1) - maddr_str = maddr_str.replace("ipfs", "p2p") - maddr = Multiaddr(maddr_str) - go_pinfo = info_from_p2p_addr(maddr) - await go_proc.expect("listening for connections", async_=True) - - key_pair = generate_new_rsa_identity() - insecure_tpt = InsecureTransport(key_pair) - host = await new_node( - key_pair=key_pair, sec_opt={PLAINTEXT_PROTOCOL_ID: insecure_tpt} - ) - await host.connect(go_pinfo) - await go_proc.expect("swarm listener accepted connection", async_=True) - s = await host.new_stream(go_pinfo.peer_id, [ECHO_PROTOCOL_ID]) - - await go_proc.expect("Got a new stream!", async_=True) - data = "data321123\n" - await s.write(data.encode()) - await go_proc.expect(f"read: {data[:-1]}", async_=True) - echoed_resp = await s.read(len(data)) - assert echoed_resp.decode() == data - await s.close() + yield call_proc finally: - go_proc.close() + for proc in procs: + proc.close() +async def make_echo_proc( + proc_factory, port: int, is_secure: bool, destination: Multiaddr = None +): + args = [f"-l={port}"] + if not is_secure: + args.append("-insecure") + if destination is not None: + args.append(f"-d={str(destination)}") + echo_proc = proc_factory(str(ECHO_PATH), args, logfile=sys.stdout, encoding="utf-8") + await echo_proc.expect(r"I am ([\w\./]+)" + NEW_LINE, async_=True) + maddr_str_ipfs = echo_proc.match.group(1) + maddr_str = maddr_str_ipfs.replace("ipfs", "p2p") + maddr = Multiaddr(maddr_str) + go_pinfo = info_from_p2p_addr(maddr) + if destination is None: + await echo_proc.expect("listening for connections", async_=True) + return echo_proc, go_pinfo + + +@pytest.mark.parametrize("num_hosts", (1,)) @pytest.mark.asyncio -async def test_insecure_conn_go_to_py(unused_tcp_port): - key_pair = generate_new_rsa_identity() - insecure_tpt = InsecureTransport(key_pair) - host = await new_node( - key_pair=key_pair, sec_opt={PLAINTEXT_PROTOCOL_ID: insecure_tpt} - ) - await host.get_network().listen(LISTEN_MADDR) +async def test_insecure_conn_py_to_go(hosts, proc_factory, unused_tcp_port): + go_proc, go_pinfo = await make_echo_proc(proc_factory, unused_tcp_port, False) + + host = hosts[0] + await host.connect(go_pinfo) + await go_proc.expect("swarm listener accepted connection", async_=True) + s = await host.new_stream(go_pinfo.peer_id, [ECHO_PROTOCOL_ID]) + + await go_proc.expect("Got a new stream!", async_=True) + data = "data321123\n" + await s.write(data.encode()) + await go_proc.expect(f"read: {data[:-1]}", async_=True) + echoed_resp = await s.read(len(data)) + assert echoed_resp.decode() == data + await s.close() + + +@pytest.mark.parametrize("num_hosts", (1,)) +@pytest.mark.asyncio +async def test_insecure_conn_go_to_py(hosts, proc_factory, unused_tcp_port): + host = hosts[0] expected_data = "Hello, world!\n" reply_data = "Replyooo!\n" event_handler_finished = asyncio.Event() @@ -76,17 +91,8 @@ async def test_insecure_conn_go_to_py(unused_tcp_port): host.set_stream_handler(ECHO_PROTOCOL_ID, _handle_echo) py_maddr = host.get_addrs()[0] - go_proc = pexpect.spawn( - str(ECHO_PATH), - [f"-l={unused_tcp_port}", "-insecure", f"-d={str(py_maddr)}"], - logfile=sys.stdout, - encoding="utf-8", - ) - try: - await go_proc.expect(r"I am ([\w\./]+)" + NEW_LINE, async_=True) - await go_proc.expect("connect with peer", async_=True) - await go_proc.expect("opened stream", async_=True) - await event_handler_finished.wait() - await go_proc.expect(f"read reply: .*{reply_data.rstrip()}.*", async_=True) - finally: - go_proc.close() + go_proc, _ = await make_echo_proc(proc_factory, unused_tcp_port, False, py_maddr) + await go_proc.expect("connect with peer", async_=True) + await go_proc.expect("opened stream", async_=True) + await event_handler_finished.wait() + await go_proc.expect(f"read reply: .*{reply_data.rstrip()}.*", async_=True) diff --git a/tests/pubsub/conftest.py b/tests/pubsub/conftest.py index 12faff3..1755ee5 100644 --- a/tests/pubsub/conftest.py +++ b/tests/pubsub/conftest.py @@ -1,38 +1,7 @@ -import asyncio - import pytest -from tests.configs import LISTEN_MADDR +from tests.factories import FloodsubFactory, GossipsubFactory, PubsubFactory from tests.pubsub.configs import GOSSIPSUB_PARAMS -from tests.pubsub.factories import ( - FloodsubFactory, - GossipsubFactory, - HostFactory, - PubsubFactory, -) - - -@pytest.fixture -def num_hosts(): - return 3 - - -@pytest.fixture -async def hosts(num_hosts): - _hosts = HostFactory.create_batch(num_hosts) - await asyncio.gather( - *[_host.get_network().listen(LISTEN_MADDR) for _host in _hosts] - ) - try: - yield _hosts - finally: - # Clean up - listeners = [] - for _host in _hosts: - for listener in _host.get_network().listeners.values(): - listener.server.close() - listeners.append(listener) - await asyncio.gather(*[listener.server.wait_closed() for listener in listeners]) @pytest.fixture diff --git a/tests/pubsub/dummy_account_node.py b/tests/pubsub/dummy_account_node.py index c2ca8bf..98a224f 100644 --- a/tests/pubsub/dummy_account_node.py +++ b/tests/pubsub/dummy_account_node.py @@ -5,8 +5,8 @@ from libp2p.host.host_interface import IHost from libp2p.pubsub.floodsub import FloodSub from libp2p.pubsub.pubsub import Pubsub from tests.configs import LISTEN_MADDR +from tests.factories import FloodsubFactory, PubsubFactory -from .factories import FloodsubFactory, PubsubFactory from .utils import message_id_generator CRYPTO_TOPIC = "ethereum" diff --git a/tests/pubsub/floodsub_integration_test_settings.py b/tests/pubsub/floodsub_integration_test_settings.py index 09e5c30..d96fc2b 100644 --- a/tests/pubsub/floodsub_integration_test_settings.py +++ b/tests/pubsub/floodsub_integration_test_settings.py @@ -3,10 +3,10 @@ import asyncio import pytest from tests.configs import LISTEN_MADDR +from tests.factories import PubsubFactory from tests.utils import cleanup, connect from .configs import FLOODSUB_PROTOCOL_ID -from .factories import PubsubFactory SUPPORTED_PROTOCOLS = [FLOODSUB_PROTOCOL_ID] diff --git a/tests/pubsub/test_floodsub.py b/tests/pubsub/test_floodsub.py index 0a67c65..7e079d1 100644 --- a/tests/pubsub/test_floodsub.py +++ b/tests/pubsub/test_floodsub.py @@ -3,9 +3,9 @@ import asyncio import pytest from libp2p.peer.id import ID +from tests.factories import FloodsubFactory from tests.utils import cleanup, connect -from .factories import FloodsubFactory from .floodsub_integration_test_settings import ( floodsub_protocol_pytest_params, perform_test_from_obj, diff --git a/tests/pubsub/test_gossipsub_backward_compatibility.py b/tests/pubsub/test_gossipsub_backward_compatibility.py index e76ce04..3f2224f 100644 --- a/tests/pubsub/test_gossipsub_backward_compatibility.py +++ b/tests/pubsub/test_gossipsub_backward_compatibility.py @@ -2,8 +2,9 @@ import functools import pytest +from tests.factories import GossipsubFactory + from .configs import FLOODSUB_PROTOCOL_ID -from .factories import GossipsubFactory from .floodsub_integration_test_settings import ( floodsub_protocol_pytest_params, perform_test_from_obj, From b2c5371323ddf12e712ad48cdaa84df6f176f7f2 Mon Sep 17 00:00:00 2001 From: mhchia Date: Thu, 29 Aug 2019 22:00:07 +0800 Subject: [PATCH 08/14] Add TODO for `Swarm.connections` --- libp2p/network/swarm.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libp2p/network/swarm.py b/libp2p/network/swarm.py index d0f2fe8..a8d1003 100644 --- a/libp2p/network/swarm.py +++ b/libp2p/network/swarm.py @@ -32,6 +32,8 @@ class Swarm(INetwork): upgrader: TransportUpgrader transport: ITransport router: IPeerRouting + # TODO: Connections an `peer_id` are 1-1 mapping in our implementation, + # whereas in Go one `peer_id` may point to multiple connections. connections: Dict[ID, IMuxedConn] listeners: Dict[str, IListener] stream_handlers: Dict[INetStream, Callable[[INetStream], None]] @@ -225,10 +227,8 @@ class Swarm(INetwork): raise SwarmException( f"fail to upgrade the connection to a muxed connection from {peer_id}" ) from error - # Store muxed_conn with peer id self.connections[peer_id] = muxed_conn - # Call notifiers since event occurred for notifee in self.notifees: await notifee.connected(self, muxed_conn) From 9ceb5f55bba03e52600b0cee9c2e04e3dac69e75 Mon Sep 17 00:00:00 2001 From: mhchia Date: Thu, 29 Aug 2019 22:08:27 +0800 Subject: [PATCH 09/14] Call `make_echo_proc` with `is_host_insecure` Use the fixture, this way we can configure `is_host_insecure` to support the test against secio. --- tests/interop/test_echo.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/tests/interop/test_echo.py b/tests/interop/test_echo.py index 513ea86..f805e23 100644 --- a/tests/interop/test_echo.py +++ b/tests/interop/test_echo.py @@ -57,8 +57,12 @@ async def make_echo_proc( @pytest.mark.parametrize("num_hosts", (1,)) @pytest.mark.asyncio -async def test_insecure_conn_py_to_go(hosts, proc_factory, unused_tcp_port): - go_proc, go_pinfo = await make_echo_proc(proc_factory, unused_tcp_port, False) +async def test_insecure_conn_py_to_go( + hosts, proc_factory, is_host_secure, unused_tcp_port +): + go_proc, go_pinfo = await make_echo_proc( + proc_factory, unused_tcp_port, is_host_secure + ) host = hosts[0] await host.connect(go_pinfo) @@ -76,7 +80,9 @@ async def test_insecure_conn_py_to_go(hosts, proc_factory, unused_tcp_port): @pytest.mark.parametrize("num_hosts", (1,)) @pytest.mark.asyncio -async def test_insecure_conn_go_to_py(hosts, proc_factory, unused_tcp_port): +async def test_insecure_conn_go_to_py( + hosts, proc_factory, is_host_secure, unused_tcp_port +): host = hosts[0] expected_data = "Hello, world!\n" reply_data = "Replyooo!\n" @@ -91,7 +97,9 @@ async def test_insecure_conn_go_to_py(hosts, proc_factory, unused_tcp_port): host.set_stream_handler(ECHO_PROTOCOL_ID, _handle_echo) py_maddr = host.get_addrs()[0] - go_proc, _ = await make_echo_proc(proc_factory, unused_tcp_port, False, py_maddr) + go_proc, _ = await make_echo_proc( + proc_factory, unused_tcp_port, is_host_secure, py_maddr + ) await go_proc.expect("connect with peer", async_=True) await go_proc.expect("opened stream", async_=True) await event_handler_finished.wait() From cec2aea9285aea394d3b9adb3ef262ea8541b24b Mon Sep 17 00:00:00 2001 From: mhchia Date: Thu, 29 Aug 2019 22:38:08 +0800 Subject: [PATCH 10/14] Move shared fixtures and constants to files --- tests/interop/conftest.py | 24 ++++++++++++++++++++++++ tests/interop/constants.py | 1 + tests/interop/test_echo.py | 29 ++++------------------------- 3 files changed, 29 insertions(+), 25 deletions(-) create mode 100644 tests/interop/conftest.py create mode 100644 tests/interop/constants.py diff --git a/tests/interop/conftest.py b/tests/interop/conftest.py new file mode 100644 index 0000000..e85f2f6 --- /dev/null +++ b/tests/interop/conftest.py @@ -0,0 +1,24 @@ +import sys + +import pexpect +import pytest + + +@pytest.fixture +def proc_factory(): + procs = [] + + def call_proc(cmd, args, logfile=None, encoding=None): + if logfile is None: + logfile = sys.stdout + if encoding is None: + encoding = "utf-8" + proc = pexpect.spawn(cmd, args, logfile=logfile, encoding=encoding) + procs.append(proc) + return proc + + try: + yield call_proc + finally: + for proc in procs: + proc.close() diff --git a/tests/interop/constants.py b/tests/interop/constants.py new file mode 100644 index 0000000..dbef043 --- /dev/null +++ b/tests/interop/constants.py @@ -0,0 +1 @@ +PEXPECT_NEW_LINE = "\r\n" diff --git a/tests/interop/test_echo.py b/tests/interop/test_echo.py index f805e23..9b170db 100644 --- a/tests/interop/test_echo.py +++ b/tests/interop/test_echo.py @@ -1,39 +1,18 @@ import asyncio import os import pathlib -import sys from multiaddr import Multiaddr -import pexpect import pytest from libp2p.peer.peerinfo import info_from_p2p_addr from libp2p.typing import TProtocol +from .constants import PEXPECT_NEW_LINE + GOPATH = pathlib.Path(os.environ["GOPATH"]) ECHO_PATH = GOPATH / "bin" / "echo" ECHO_PROTOCOL_ID = TProtocol("/echo/1.0.0") -NEW_LINE = "\r\n" - - -@pytest.fixture -def proc_factory(): - procs = [] - - def call_proc(cmd, args, logfile=None, encoding=None): - if logfile is None: - logfile = sys.stdout - if encoding is None: - encoding = "utf-8" - proc = pexpect.spawn(cmd, args, logfile=logfile, encoding=encoding) - procs.append(proc) - return proc - - try: - yield call_proc - finally: - for proc in procs: - proc.close() async def make_echo_proc( @@ -44,8 +23,8 @@ async def make_echo_proc( args.append("-insecure") if destination is not None: args.append(f"-d={str(destination)}") - echo_proc = proc_factory(str(ECHO_PATH), args, logfile=sys.stdout, encoding="utf-8") - await echo_proc.expect(r"I am ([\w\./]+)" + NEW_LINE, async_=True) + echo_proc = proc_factory(str(ECHO_PATH), args) + await echo_proc.expect(r"I am ([\w\./]+)" + PEXPECT_NEW_LINE, async_=True) maddr_str_ipfs = echo_proc.match.group(1) maddr_str = maddr_str_ipfs.replace("ipfs", "p2p") maddr = Multiaddr(maddr_str) From 1e59438f25264e1e593867d107692b8cf9c69a43 Mon Sep 17 00:00:00 2001 From: Kevin Mai-Husan Chia Date: Sat, 31 Aug 2019 22:32:32 +0800 Subject: [PATCH 11/14] Update libp2p/network/swarm.py Co-Authored-By: NIC Lin --- libp2p/network/swarm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libp2p/network/swarm.py b/libp2p/network/swarm.py index a8d1003..7517132 100644 --- a/libp2p/network/swarm.py +++ b/libp2p/network/swarm.py @@ -32,7 +32,7 @@ class Swarm(INetwork): upgrader: TransportUpgrader transport: ITransport router: IPeerRouting - # TODO: Connections an `peer_id` are 1-1 mapping in our implementation, + # TODO: Connection and `peer_id` are 1-1 mapping in our implementation, # whereas in Go one `peer_id` may point to multiple connections. connections: Dict[ID, IMuxedConn] listeners: Dict[str, IListener] From 9e8a6bdf293d7c03512de297e90f3883f7af3fb2 Mon Sep 17 00:00:00 2001 From: Kevin Mai-Husan Chia Date: Sat, 31 Aug 2019 22:32:43 +0800 Subject: [PATCH 12/14] Update tests/conftest.py Co-Authored-By: NIC Lin --- tests/conftest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/conftest.py b/tests/conftest.py index 9101fa6..fd753be 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -26,7 +26,7 @@ async def hosts(num_hosts, is_host_secure): yield _hosts finally: # TODO: It's possible that `close` raises exceptions currently, - # due to the connection reset things. Though we are not so careful about that when + # due to the connection reset things. Though we don't care much about that when # cleaning up the tasks, it is probably better to handle the exceptions properly. await asyncio.gather( *[_host.close() for _host in _hosts], return_exceptions=True From aa0866698f24ac2856521c53fa939f151f2a3100 Mon Sep 17 00:00:00 2001 From: mhchia Date: Sat, 31 Aug 2019 22:37:59 +0800 Subject: [PATCH 13/14] PR feedback: Add check in `Swarm.close_peer` --- libp2p/network/swarm.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libp2p/network/swarm.py b/libp2p/network/swarm.py index 7517132..300ad71 100644 --- a/libp2p/network/swarm.py +++ b/libp2p/network/swarm.py @@ -279,6 +279,8 @@ class Swarm(INetwork): ) async def close_peer(self, peer_id: ID) -> None: + if peer_id not in self.connections: + return connection = self.connections[peer_id] del self.connections[peer_id] await connection.close() From b955c0fa02a97b9cc10794ca689fdaefa700e1ea Mon Sep 17 00:00:00 2001 From: mhchia Date: Sat, 31 Aug 2019 22:38:46 +0800 Subject: [PATCH 14/14] Explicitly import ID, Transport from secio --- tests/factories.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/tests/factories.py b/tests/factories.py index 0604094..240bdb8 100644 --- a/tests/factories.py +++ b/tests/factories.py @@ -10,7 +10,7 @@ from libp2p.pubsub.gossipsub import GossipSub from libp2p.pubsub.pubsub import Pubsub from libp2p.security.base_transport import BaseSecureTransport from libp2p.security.insecure.transport import PLAINTEXT_PROTOCOL_ID, InsecureTransport -from libp2p.security.secio.transport import ID, Transport +import libp2p.security.secio.transport as secio from libp2p.typing import TProtocol from tests.pubsub.configs import ( FLOODSUB_PROTOCOL_ID, @@ -22,15 +22,10 @@ from tests.pubsub.configs import ( def security_transport_factory( is_secure: bool, key_pair: KeyPair ) -> Dict[TProtocol, BaseSecureTransport]: - protocol_id: TProtocol - security_transport: BaseSecureTransport if not is_secure: - protocol_id = PLAINTEXT_PROTOCOL_ID - security_transport = InsecureTransport(key_pair) + return {PLAINTEXT_PROTOCOL_ID: InsecureTransport(key_pair)} else: - protocol_id = ID - security_transport = Transport(key_pair) - return {protocol_id: security_transport} + return {secio.ID: secio.Transport(key_pair)} def swarm_factory(is_secure: bool):