forked from cerc-io/ipld-eth-server
update dependencies to work with update eth-block-extractor
This commit is contained in:
+4
@@ -0,0 +1,4 @@
|
||||
*.swp
|
||||
examples/echo/echo
|
||||
examples/multicodecs/multicodecs
|
||||
.idea
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
os:
|
||||
- linux
|
||||
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.11.x
|
||||
|
||||
env:
|
||||
global:
|
||||
- GOTFLAGS="-race"
|
||||
matrix:
|
||||
- BUILD_DEPTYPE=gomod
|
||||
|
||||
|
||||
# disable travis install
|
||||
install:
|
||||
- true
|
||||
|
||||
script:
|
||||
- bash <(curl -s https://raw.githubusercontent.com/ipfs/ci-helpers/master/travis-ci/run-standard-tests.sh)
|
||||
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $GOPATH/pkg/mod
|
||||
- $HOME/.cache/go-build
|
||||
|
||||
notifications:
|
||||
email: false
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Juan Batiz-Benet
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
+304
@@ -0,0 +1,304 @@
|
||||
# go-libp2p release notes
|
||||
|
||||
## 6.0.0
|
||||
|
||||
We're pleased to announce go-libp2p 6.0.0. This release includes a massive
|
||||
refactor of go-libp2p that paves the way for new transports such as QUIC.
|
||||
Unfortunately, as it is broad sweeping, there are some breaking changes,
|
||||
*especially* for maintainers of custom transports.
|
||||
|
||||
Below, we cover the changes you'll likely care about. For convenience, we've
|
||||
broken this into a section for users and transport authors/maintainers. However,
|
||||
transport maintainers should really read both sections.
|
||||
|
||||
### For Users
|
||||
|
||||
Libp2p users should be aware of a few major changes.
|
||||
|
||||
* Guarantees and performance concerning connect/disconnect notification
|
||||
processing have improved.
|
||||
* Handling of half-closed streams has changed (READ THIS SECTION).
|
||||
* Some constructors and method signatures have changed slightly.
|
||||
|
||||
#### Dialing And Source Addresses
|
||||
|
||||
We've improved the logic that selects the source address when dialing. In the
|
||||
past, you may have run into an issue where you couldn't dial non-local nodes
|
||||
when listening on 127.0.0.1 as opposed to 0.0.0.0. This happened because
|
||||
go-libp2p would randomly pick the source address from the set of addresses on
|
||||
which the node was listening. We did this to ensure that the other end could
|
||||
dial us back at the same address. Unfortunately, one can't use 127.0.0.1 as a
|
||||
source address when dialing any non-local address so outbound dials failed.
|
||||
|
||||
go-libp2p now tries to be smarter about this and avoids picking source addresses
|
||||
that have no route to the destination address.
|
||||
|
||||
#### Bandwidth Metrics
|
||||
|
||||
To start out on an unhappy note, bandwidth metrics are now less accurate. In the
|
||||
past, transports returned "minimal" connections (e.g., a TCP connection) so we
|
||||
could wrap these transport connections in "metrics" connections that counted
|
||||
every byte sent and received.
|
||||
|
||||
Unfortunately, now that we've moved encryption and multiplexing down into the
|
||||
transport layer, the connection we're wrapping has significantly more
|
||||
under-the-covers overhead.
|
||||
|
||||
However, we do hope to improve this and get even *better* bandwidth metrics than
|
||||
we did before. See [libp2p/go-libp2p-transport#31][] for details.
|
||||
|
||||
[libp2p/go-libp2p-transport#31]: https://github.com/libp2p/go-libp2p-transport/issues/31
|
||||
|
||||
#### Notifications
|
||||
|
||||
This release brings performance improvements and easy to reason about ordering
|
||||
guarantees libp2p connect/disconnect events:
|
||||
|
||||
1. For any given connection/stream, libp2p will wait for all connect/open event
|
||||
handlers to finish exit before triggering a disconnect/close event for the
|
||||
connection/stream.
|
||||
2. When a user calls the Close (or `Reset`) method on a connection or stream,
|
||||
go-libp2p will process the close event asynchronously (i.e., not block the
|
||||
call to `Close`). Otherwise, a call to `Close` from within a connect event
|
||||
handler would deadlock.
|
||||
3. Unless otherwise noted, events will be handled in parallel.
|
||||
|
||||
What does this mean for end users? Well:
|
||||
|
||||
1. Reference counting connections to a peer using connect/disconnect events
|
||||
should "just work" and should never go negative.
|
||||
2. Under heavy connect/disconnect loads, connecting to new peers should be
|
||||
faster (usually).
|
||||
|
||||
For those interested in the history of this issue, ...
|
||||
|
||||
In the past, (dis)connect and stream open/close notifications have been a bit of
|
||||
a pain point. For a long time, they were fired off in parallel and one could, for
|
||||
example, process a disconnect notification before a connect notification (we had
|
||||
to support *negative* ref-counts in several places to account for this).
|
||||
|
||||
After no end of trouble, we finally "fixed" this by synchronizing notification
|
||||
delivery. We still delivered notifications to all notifiees in parallel, we just
|
||||
processed the events in series.
|
||||
|
||||
Unfortunately, under heavy connect/disconnect load, new connections could easily
|
||||
get stuck on open behind a queue of connect events all being handled in series.
|
||||
In theory, these events should have been handled quickly but in practice, it's
|
||||
very hard to avoid locks *entirely* (bitswap's event handlers were especially
|
||||
problematic).
|
||||
|
||||
Worse, this serial delivery guarantee didn't actually provide us with an
|
||||
*in-order* delivery guarantee as it was still possible for a disconnect to
|
||||
happen before we even *started* to fire the connect event. The situation was
|
||||
slightly better than before because the events couldn't overlap but still far
|
||||
from optimal.
|
||||
|
||||
However, this has all been resolved now. From now on, you'll never receive a
|
||||
disconnect event before a connect event.
|
||||
|
||||
#### Conn.GetStreams Signature Change
|
||||
|
||||
The signature of the `GetStreams` method on `go-libp2p-net.Conn` has changed from:
|
||||
|
||||
```go
|
||||
GetStreams() ([]Stream, error)
|
||||
```
|
||||
|
||||
To:
|
||||
|
||||
```go
|
||||
GetStreams() []Stream
|
||||
```
|
||||
|
||||
Listing the streams on an open connection should never involve IO or do anything
|
||||
that can fail so we removed this error to improve usability.
|
||||
|
||||
#### Libp2p Constructor
|
||||
|
||||
If you're not already doing so, you should be using the `libp2p.New` constructor
|
||||
to make your libp2p nodes. This release brings quite a few new options to the
|
||||
libp2p constructor so if it hasn't been flexible enough for you in the past, I
|
||||
recommend that you try again. A simple example can be found in the
|
||||
[echo][example:echo] example.
|
||||
|
||||
Given this work and in an attempt to consolidate all of our configuration logic
|
||||
in one place, we've removed all default transports from go-libp2p-swarm.
|
||||
|
||||
TL;DR: Please use the libp2p constructor.
|
||||
|
||||
#### Zombie Streams
|
||||
|
||||
From this release on, when you're done with a stream, you must either call
|
||||
`Reset` on it (in case of an error) or close it and read an EOF (or some other
|
||||
error). Otherwise, libp2p can't determine if the stream is *actually* closed and
|
||||
will hang onto it indefinitely.
|
||||
|
||||
To make properly closing streams a bit easier, we've added two methods to
|
||||
[go-libp2p-net][]: `AwaitEOF` and `FullClose`.
|
||||
|
||||
* `AwaitEOF(stream)` tries to read a single byte from the stream. If `Read`
|
||||
returns an EOF, `AwaitEOF` returns success. Otherwise, if `Read` either reads
|
||||
some data or returns some other error, `AwaitEOF` resets the stream and returns
|
||||
an error. To avoid waiting indefinitely, `AwaitEOF` resets the stream
|
||||
unconditionally after 1 minute.
|
||||
* `FullClose(stream)` is a convenience function that closes the stream and then
|
||||
calls `AwaitEOF` on it.
|
||||
|
||||
Like with libp2p notifications, this issue has a bit of history...
|
||||
|
||||
In the beginning, libp2p assumed that calling `Close` on a stream would close
|
||||
the stream for both reading and writing. Unfortunately, *none* of our stream
|
||||
multiplexers actually behaved this way. In practice, `Close` always closed the
|
||||
stream for writing.
|
||||
|
||||
After realizing this, we made two changes:
|
||||
|
||||
1. We accepted the fact that `Close` only closed the stream for writing.
|
||||
2. We added a `Reset` method for killing the stream (closing it in both
|
||||
directions, throwing away any buffered data).
|
||||
|
||||
However, we ran into a bit of a snag because we try to track open streams and
|
||||
need some way to tell when a stream has been closed. In the past this was easy:
|
||||
when the user calls `Close` on the stream, stop tracking it. However, now that
|
||||
`Close` only closes the stream for writing, we still *technically* needed to
|
||||
track it until the *other* end closed the stream as well. Unfortunately, without
|
||||
actually reading from the stream, we have no way of knowing about this.
|
||||
Therefore, if the user calls `Close` on a stream and then walks away, we'd have
|
||||
to hang onto the stream indefinitely.
|
||||
|
||||
Our solution was to simply stop tracking streams once they were closed for
|
||||
writing. This wasn't the *correct* behavior but it avoided leaking memory in the
|
||||
common case:
|
||||
|
||||
1. The user calls `Close` and drops all references to the stream.
|
||||
2. The other end calls `Close` without writing any additional data.
|
||||
3. The stream multiplexer observes both closes and drops *its* reference to the stream.
|
||||
4. The garbage collector garbage collects the stream.
|
||||
|
||||
However, this meant that:
|
||||
|
||||
1. The list of "open" streams was technically incomplete.
|
||||
2. If the other side either failed to call `Close` or tried to send data before
|
||||
closing, the stream would remain "open" (until the connection was closed).
|
||||
|
||||
In this release, we've changed this behavior. Now, when you `Close` a stream for
|
||||
writing, libp2p *continues* to track it. We only stop tracking it when either:
|
||||
|
||||
1. You call `Reset` (throwing away the stream).
|
||||
2. You finish reading any data off of it and observe either an EOF or an error.
|
||||
|
||||
This way, we never "forget" about open streams or leave them in a half-forgotten
|
||||
state.
|
||||
|
||||
In the future, I'd like to add a `CloseAndForget` method to streams that:
|
||||
|
||||
1. Closes the stream (sends an EOF).
|
||||
2. Tells the swarm to stop tracking the stream.
|
||||
3. Tells the stream muxer to stop tracking the stream and throw away any data
|
||||
the other side may send (possibly resetting the stream on unexpected data).
|
||||
|
||||
However:
|
||||
|
||||
1. This would likely require modifying our stream muxers which may not be
|
||||
feasible.
|
||||
2. Explicitly waiting for an EOF is still the correct thing to do unless you
|
||||
really don't care if the operation succeeded.
|
||||
|
||||
### For Transport Maintainers
|
||||
|
||||
For transport maintainers, quite a bit has changed. Before this change,
|
||||
transports created simple, unencrypted, stream connections and it was the job of
|
||||
the libp2p Network (go-libp2p-swarm) to negotiate security, multiplexing, etc.
|
||||
|
||||
However, when attempting to add support for the QUIC protocol, we realized that
|
||||
this was going to be a problem: QUIC already handles authentication and
|
||||
encryption (using TLS1.3) and multiplexing. After much debate, we inverted our
|
||||
current architecture and made transports responsible for encrypting/multiplexing
|
||||
their connections (before returning them).
|
||||
|
||||
To make this palatable, we've also introduced a new ["upgrader"
|
||||
library][go-libp2p-transport-upgrader] for upgrading go-multiaddr-net
|
||||
connections/listeners to full libp2p transport connections/listeners. Transports
|
||||
that don't support encryption/multiplexing out of the box can expect to have an
|
||||
upgrader passed into the constructor.
|
||||
|
||||
To get a feel for how this new transport system works, take a look at the TCP
|
||||
and WebSocket transports and the transport interface documentation:
|
||||
|
||||
* [TCP Transport][go-tcp-transport]
|
||||
* [WebSocket Transport][go-ws-transport]
|
||||
* [Transport Interface][doc:go-libp2p-transport]
|
||||
|
||||
#### Deprecated Packages
|
||||
|
||||
This release sees the deprecation of a few packages:
|
||||
|
||||
* [go-peerstream][] has been deprecated and all functionality has been merged
|
||||
into [go-libp2p-swarm][]. [go-peerstream][] was written as a general-purpose
|
||||
(not libp2p specific) listener, connection, and stream manager. However, this
|
||||
package caused more problems than it solved and was incompatible with the new
|
||||
transport interface.
|
||||
* [go-libp2p-interface-conn][] has been deprecated. These interfaces to bridge
|
||||
the gap between transport-level connections and [go-libp2p-net][] connections
|
||||
however, now that transport connections are fully multiplexed/encrypted, this
|
||||
is no longer needed.
|
||||
* [go-libp2p-conn][] has also been deprecated and most of the functionality has
|
||||
been moved to [go-libp2p-transport-upgrader][]. This package used to provide
|
||||
connection "upgrade" logic for upgrading transport-level connections to
|
||||
[go-libp2p-interface-conn][] connections however, transport-level connections
|
||||
now provide the required functionality out of the box.
|
||||
|
||||
#### Testing
|
||||
|
||||
We've moved `GenSwarmNetwork` in [go-libp2p-netutil][] to `GenSwarm` in
|
||||
[go-libp2p-swarm/testing][] because:
|
||||
|
||||
1. The swarm duplicated this exact function for its own tests.
|
||||
2. The swarm couldn't depend on [go-libp2p-netutil][] because
|
||||
[go-libp2p-netutil][] depends on [go-libp2p-swarm][].
|
||||
|
||||
We've also added a new transport test suit
|
||||
[go-libp2p-transport/test][]. If you implement a new transport, please consider
|
||||
testing against these suite. If you find a bug in an existing transport, please
|
||||
consider adding a test to this suite.
|
||||
|
||||
#### go-addr-util
|
||||
|
||||
In go-addr-util, we've removed the `SupportedTransportStrings` and
|
||||
`SupportedTransportProtocols` transport registries and the associated
|
||||
`AddTransport` function. These registries were updated by `init` functions in
|
||||
packages providing transports and were used to keep track of known transports.
|
||||
|
||||
However, *importing* a transport doesn't mean any libp2p nodes have been
|
||||
configured to actually *use* that transport. Therefore, in the new go-libp2p,
|
||||
it's go-libp2p-swarm's job to keep track of which transports are supported
|
||||
(i.e., which transports have been registered with the swarm).
|
||||
|
||||
We've also removed the associated `AddrUsable`, `FilterUsableAddrs`, and
|
||||
`AddrUsableFunc` functions.
|
||||
|
||||
#### Pluggable Security Transports
|
||||
|
||||
This release brings a new pluggable security transport framework. Implementing a
|
||||
new security framework is now as simple as:
|
||||
|
||||
1. Implement the interfaces defined in [go-conn-security][].
|
||||
2. Pass it into the libp2p constructor using the `Security` option.
|
||||
|
||||
[go-conn-security]: https://github.com/libp2p/go-conn-security
|
||||
[go-libp2p-conn]: https://github.com/libp2p/go-libp2p-conn
|
||||
[go-libp2p-interface-conn]: https://github.com/libp2p/go-libp2p-interface-conn
|
||||
[go-libp2p-net]: https://github.com/libp2p/go-libp2p-net
|
||||
[go-libp2p-netutil]: https://github.com/libp2p/go-libp2p-netutil
|
||||
[go-libp2p-swarm]: https://github.com/libp2p/go-libp2p-swarm
|
||||
[go-libp2p-swarm/testing]: https://github.com/libp2p/go-libp2p-swarm/tree/master/testing
|
||||
[go-libp2p-transport]: https://github.com/libp2p/go-libp2p-transport
|
||||
[go-libp2p-transport/test]: https://github.com/libp2p/go-libp2p-transport/tree/master/test
|
||||
[go-libp2p-transport-upgrader]: https://github.com/libp2p/go-libp2p-transport-upgrader
|
||||
[go-peerstream]: https://github.com/libp2p/go-peerstream
|
||||
[go-tcp-transport]: https://github.com/libp2p/go-tcp-transport
|
||||
[go-ws-transport]: https://github.com/libp2p/go-ws-transport
|
||||
|
||||
[example:echo]: https://github.com/libp2p/go-libp2p-examples/tree/master/echo
|
||||
|
||||
[doc:go-libp2p-transport]: https://godoc.org/github.com/libp2p/go-libp2p-transport
|
||||
+214
@@ -0,0 +1,214 @@
|
||||
|
||||
<h1 align="center">
|
||||
<a href="libp2p.io"><img width="250" src="https://github.com/libp2p/libp2p/blob/master/logo/black-bg-2.png?raw=true" alt="libp2p hex logo" /></a>
|
||||
</h1>
|
||||
|
||||
<h3 align="center">The Go implementation of the libp2p Networking Stack.</h3>
|
||||
|
||||
<p align="center">
|
||||
<a href="http://protocol.ai"><img src="https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square" /></a>
|
||||
<a href="http://libp2p.io/"><img src="https://img.shields.io/badge/project-libp2p-yellow.svg?style=flat-square" /></a>
|
||||
<a href="http://webchat.freenode.net/?channels=%23libp2p"><img src="https://img.shields.io/badge/freenode-%23libp2p-yellow.svg?style=flat-square" /></a>
|
||||
<a href="https://waffle.io/libp2p/libp2p"><img src="https://img.shields.io/badge/pm-waffle-yellow.svg?style=flat-square" /></a>
|
||||
<a href="https://discuss.libp2p.io"><img src="https://img.shields.io/discourse/https/discuss.libp2p.io/posts.svg"/></a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://travis-ci.com/libp2p/go-libp2p"><img src="https://travis-ci.com/libp2p/go-libp2p.svg?branch=master" /></a>
|
||||
<br>
|
||||
<a href="https://github.com/RichardLitt/standard-readme"><img src="https://img.shields.io/badge/standard--readme-OK-green.svg?style=flat-square" /></a>
|
||||
<a href="https://godoc.org/github.com/libp2p/go-libp2p"><img src="https://godoc.org/github.com/libp2p/go-libp2p?status.svg" /></a>
|
||||
<a href=""><img src="https://img.shields.io/badge/golang-%3E%3D1.8.0-orange.svg?style=flat-square" /></a>
|
||||
<br>
|
||||
</p>
|
||||
|
||||
# Project status
|
||||
|
||||
[](https://waffle.io/libp2p/go-libp2p/metrics/throughput)
|
||||
|
||||
[**`Weekly Core Dev Calls`**](https://github.com/ipfs/pm/issues/674)
|
||||
|
||||
# Table of Contents
|
||||
|
||||
- [Background](#background)
|
||||
- [Usage](#usage)
|
||||
- [API](#api)
|
||||
- [Examples](#examples)
|
||||
- [Development](#development)
|
||||
- [Using the libp2p Workspace](#using-the-libp2p-workspace)
|
||||
- [About gx](#about-gx)
|
||||
- [Tests](#tests)
|
||||
- [Packages](#packages)
|
||||
- [Contribute](#contribute)
|
||||
|
||||
## Background
|
||||
|
||||
[libp2p](https://github.com/libp2p/specs) is a networking stack and library modularized out of [The IPFS Project](https://github.com/ipfs/ipfs), and bundled separately for other tools to use.
|
||||
>
|
||||
libp2p is the product of a long, and arduous quest of understanding -- a deep dive into the internet's network stack, and plentiful peer-to-peer protocols from the past. Building large scale peer-to-peer systems has been complex and difficult in the last 15 years, and libp2p is a way to fix that. It is a "network stack" -- a protocol suite -- that cleanly separates concerns, and enables sophisticated applications to only use the protocols they absolutely need, without giving up interoperability and upgradeability. libp2p grew out of IPFS, but it is built so that lots of people can use it, for lots of different projects.
|
||||
>
|
||||
> We will be writing a set of docs, posts, tutorials, and talks to explain what p2p is, why it is tremendously useful, and how it can help your existing and new projects. But in the meantime, check out
|
||||
>
|
||||
> - [**Our developing collection of docs**](https://docs.libp2p.io)
|
||||
> - [**Our community discussion forums**](https://discuss.libp2p.io)
|
||||
> - [**The libp2p Specification**](https://github.com/libp2p/specs)
|
||||
> - [**go-libp2p implementation**](https://github.com/libp2p/go-libp2p)
|
||||
> - [**js-libp2p implementation**](https://github.com/libp2p/js-libp2p)
|
||||
> - [**rust-libp2p implementation**](https://github.com/libp2p/rust-libp2p)
|
||||
|
||||
## Usage
|
||||
|
||||
This repository (`go-libp2p`) serves as the entrypoint to the universe of modules that compose the Go implementation of the libp2p stack.
|
||||
|
||||
We mainly use [Go modules](https://github.com/golang/go/wiki/Modules) for our dependency and release management (and thus require go >= 1.11). In order to get the best developer experience, we recommend you do too. Otherwise, you may ocassionally encounter a breaking build as you'll be running off master (which, by definition, is not guaranteed to be stable).
|
||||
|
||||
You can start using go-libp2p in your Go application simply by adding imports from our repos, e.g.:
|
||||
|
||||
```go
|
||||
import "github.com/libp2p/go-libp2p"
|
||||
```
|
||||
|
||||
The next time you run `go get` or `go build`, the Go build tools will look for [available releases](https://github.com/libp2p/go-libp2p/releases), and will pick the highest available one.
|
||||
|
||||
As new releases of go-libp2p are made available, you can upgrade your application by manually editing your `go.mod` file, or using the [Go tools](https://golang.org/cmd/go/#hdr-Maintaining_module_requirements) to maintain module requirements.
|
||||
|
||||
### API
|
||||
|
||||
[](https://godoc.org/github.com/libp2p/go-libp2p)
|
||||
|
||||
### Examples
|
||||
|
||||
Examples can be found in the [examples repo](https://github.com/libp2p/go-libp2p-examples).
|
||||
|
||||
## Development
|
||||
|
||||
### Using the libp2p Workspace
|
||||
|
||||
While developing, you may need to make changes to several modules at once, or you may want changes made locally in one module to be available for import by another.
|
||||
|
||||
The [go libp2p workspace](https://github.com/libp2p/workspace-go-libp2p) provides a developer-oriented view of the modules that comprise go-libp2p.
|
||||
|
||||
Using the tooling in the workspace repository, you can checkout all of go-libp2p's module repos and enter "local mode", which adds [replace directives](https://github.com/golang/go/wiki/Modules#gomod) to the go.mod files in each local working copy. When you build locally, the libp2p depdendencies will be resolved from your local working copies.
|
||||
|
||||
Once you've committed your changes, you can switch back to "remote mode", which removes the replace directives and pulls imports from the main go module cache.
|
||||
|
||||
See the [workspace repo](https://github.com/libp2p/workspace-go-libp2p) for more information.
|
||||
|
||||
### About gx
|
||||
|
||||
Before adopting gomod, libp2p used [gx](https://github.com/whyrusleeping/gx) to manage dependencies using [IPFS](https://ipfs.io).
|
||||
|
||||
Due to the difficulties in keeping both dependency management solutions up-to-date, gx support was ended in April 2019.
|
||||
|
||||
Ending gx support does not mean that existing gx builds will break. Because gx references dependencies by their immutable IPFS hash, any currently working gx builds will continue to work for as long as the dependencies are resolvable in IPFS.
|
||||
|
||||
However, new changes to go-libp2p will not be published via gx, and users are encouraged to adopt gomod to stay up-to-date.
|
||||
|
||||
If you experience any issues migrating from gx to gomod, please [join the discussion at the libp2p forums](https://discuss.libp2p.io/t/gomod-and-go-libp2p/44).
|
||||
|
||||
### Tests
|
||||
|
||||
`go test ./...` will run all tests in the repo.
|
||||
|
||||
### Packages
|
||||
|
||||
> This table is generated using the module [`package-table`](https://github.com/ipfs-shipyard/package-table) with `package-table --data=package-list.json`.
|
||||
|
||||
List of packages currently in existence for libp2p:
|
||||
|
||||
| Name | CI | Coverage | Description |
|
||||
| ---------|---------|---------|--------- |
|
||||
| **Libp2p** |
|
||||
| [`go-libp2p`](//github.com/libp2p/go-libp2p) | [](https://travis-ci.com/libp2p/go-libp2p) | [](https://codecov.io/gh/libp2p/go-libp2p) | go-libp2p entry point |
|
||||
| [`go-libp2p-host`](//github.com/libp2p/go-libp2p-host) | [](https://travis-ci.com/libp2p/go-libp2p-host) | [](https://codecov.io/gh/libp2p/go-libp2p-host) | libp2p "host" interface |
|
||||
| [`go-libp2p-blankhost`](//github.com/libp2p/go-libp2p-blankhost) | [](https://travis-ci.com/libp2p/go-libp2p-blankhost) | [](https://codecov.io/gh/libp2p/go-libp2p-blankhost) | minimal implementation of the "host" interface |
|
||||
| **Network** |
|
||||
| [`go-libp2p-net`](//github.com/libp2p/go-libp2p-net) | [](https://travis-ci.com/libp2p/go-libp2p-net) | [](https://codecov.io/gh/libp2p/go-libp2p-net) | libp2p connection and "network" interfaces |
|
||||
| [`go-libp2p-swarm`](//github.com/libp2p/go-libp2p-swarm) | [](https://travis-ci.com/libp2p/go-libp2p-swarm) | [](https://codecov.io/gh/libp2p/go-libp2p-swarm) | reference implementation |
|
||||
| **Transport** |
|
||||
| [`go-libp2p-transport`](//github.com/libp2p/go-libp2p-transport) | [](https://travis-ci.com/libp2p/go-libp2p-transport) | [](https://codecov.io/gh/libp2p/go-libp2p-transport) | interfaces |
|
||||
| [`go-ws-transport`](//github.com/libp2p/go-ws-transport) | [](https://travis-ci.com/libp2p/go-ws-transport) | [](https://codecov.io/gh/libp2p/go-ws-transport) | WebSocket transport |
|
||||
| [`go-tcp-transport`](//github.com/libp2p/go-tcp-transport) | [](https://travis-ci.com/libp2p/go-tcp-transport) | [](https://codecov.io/gh/libp2p/go-tcp-transport) | TCP transport |
|
||||
| [`go-libp2p-quic-transport`](//github.com/libp2p/go-libp2p-quic-transport) | [](https://travis-ci.com/libp2p/go-libp2p-quic-transport) | [](https://codecov.io/gh/libp2p/go-libp2p-quic-transport) | QUIC transport |
|
||||
| [`go-udp-transport`](//github.com/libp2p/go-udp-transport) | [](https://travis-ci.com/libp2p/go-udp-transport) | [](https://codecov.io/gh/libp2p/go-udp-transport) | UDP transport |
|
||||
| [`go-utp-transport`](//github.com/libp2p/go-utp-transport) | [](https://travis-ci.com/libp2p/go-utp-transport) | [](https://codecov.io/gh/libp2p/go-utp-transport) | uTorrent transport (UTP) |
|
||||
| [`go-libp2p-circuit`](//github.com/libp2p/go-libp2p-circuit) | [](https://travis-ci.com/libp2p/go-libp2p-circuit) | [](https://codecov.io/gh/libp2p/go-libp2p-circuit) | relay transport |
|
||||
| [`go-libp2p-transport-upgrader`](//github.com/libp2p/go-libp2p-transport-upgrader) | [](https://travis-ci.com/libp2p/go-libp2p-transport-upgrader) | [](https://codecov.io/gh/libp2p/go-libp2p-transport-upgrader) | upgrades multiaddr-net connections into full libp2p transports |
|
||||
| [`go-libp2p-reuseport-transport`](//github.com/libp2p/go-libp2p-reuseport-transport) | [](https://travis-ci.com/libp2p/go-libp2p-reuseport-transport) | [](https://codecov.io/gh/libp2p/go-libp2p-reuseport-transport) | partial transport for building transports that reuse ports |
|
||||
| **Encrypted Channels** |
|
||||
| [`go-conn-security`](//github.com/libp2p/go-conn-security) | [](https://travis-ci.com/libp2p/go-conn-security) | [](https://codecov.io/gh/libp2p/go-conn-security) | interfaces |
|
||||
| [`go-libp2p-secio`](//github.com/libp2p/go-libp2p-secio) | [](https://travis-ci.com/libp2p/go-libp2p-secio) | [](https://codecov.io/gh/libp2p/go-libp2p-secio) | SecIO crypto channel |
|
||||
| [`go-conn-security-multistream`](//github.com/libp2p/go-conn-security-multistream) | [](https://travis-ci.com/libp2p/go-conn-security-multistream) | [](https://codecov.io/gh/libp2p/go-conn-security-multistream) | multistream multiplexed meta crypto channel |
|
||||
| **Private Network** |
|
||||
| [`go-libp2p-interface-pnet`](//github.com/libp2p/go-libp2p-interface-pnet) | [](https://travis-ci.com/libp2p/go-libp2p-interface-pnet) | [](https://codecov.io/gh/libp2p/go-libp2p-interface-pnet) | interfaces |
|
||||
| [`go-libp2p-pnet`](//github.com/libp2p/go-libp2p-pnet) | [](https://travis-ci.com/libp2p/go-libp2p-pnet) | [](https://codecov.io/gh/libp2p/go-libp2p-pnet) | reference implementation |
|
||||
| **Stream Muxers** |
|
||||
| [`go-stream-muxer`](//github.com/libp2p/go-stream-muxer) | [](https://travis-ci.com/libp2p/go-stream-muxer) | [](https://codecov.io/gh/libp2p/go-stream-muxer) | interfaces |
|
||||
| [`go-smux-yamux`](//github.com/whyrusleeping/go-smux-yamux) | [](https://travis-ci.com/whyrusleeping/go-smux-yamux) | [](https://codecov.io/gh/whyrusleeping/go-smux-yamux) | YAMUX stream multiplexer |
|
||||
| [`go-smux-multiplex`](//github.com/whyrusleeping/go-smux-multiplex) | [](https://travis-ci.com/whyrusleeping/go-smux-multiplex) | [](https://codecov.io/gh/whyrusleeping/go-smux-multiplex) | MPLEX stream multiplexer |
|
||||
| **NAT Traversal** |
|
||||
| [`go-libp2p-nat`](//github.com/libp2p/go-libp2p-nat) | [](https://travis-ci.com/libp2p/go-libp2p-nat) | [](https://codecov.io/gh/libp2p/go-libp2p-nat) | |
|
||||
| **Peerstore** |
|
||||
| [`go-libp2p-peerstore`](//github.com/libp2p/go-libp2p-peerstore) | [](https://travis-ci.com/libp2p/go-libp2p-peerstore) | [](https://codecov.io/gh/libp2p/go-libp2p-peerstore) | interfaces and reference implementation |
|
||||
| **Connection Manager** |
|
||||
| [`go-libp2p-interface-connmgr`](//github.com/libp2p/go-libp2p-interface-connmgr) | [](https://travis-ci.com/libp2p/go-libp2p-interface-connmgr) | [](https://codecov.io/gh/libp2p/go-libp2p-interface-connmgr) | interface |
|
||||
| [`go-libp2p-connmgr`](//github.com/libp2p/go-libp2p-connmgr) | [](https://travis-ci.com/libp2p/go-libp2p-connmgr) | [](https://codecov.io/gh/libp2p/go-libp2p-connmgr) | reference implementation |
|
||||
| **Routing** |
|
||||
| [`go-libp2p-routing`](//github.com/libp2p/go-libp2p-routing) | [](https://travis-ci.com/libp2p/go-libp2p-routing) | [](https://codecov.io/gh/libp2p/go-libp2p-routing) | routing interfaces |
|
||||
| [`go-libp2p-record`](//github.com/libp2p/go-libp2p-record) | [](https://travis-ci.com/libp2p/go-libp2p-record) | [](https://codecov.io/gh/libp2p/go-libp2p-record) | record type and validator logic |
|
||||
| [`go-libp2p-routing-helpers`](//github.com/libp2p/go-libp2p-routing-helpers) | [](https://travis-ci.com/libp2p/go-libp2p-routing-helpers) | [](https://codecov.io/gh/libp2p/go-libp2p-routing-helpers) | helpers for composing routers |
|
||||
| [`go-libp2p-kad-dht`](//github.com/libp2p/go-libp2p-kad-dht) | [](https://travis-ci.com/libp2p/go-libp2p-kad-dht) | [](https://codecov.io/gh/libp2p/go-libp2p-kad-dht) | Kademlia-like router |
|
||||
| [`go-libp2p-pubsub-router`](//github.com/libp2p/go-libp2p-pubsub-router) | [](https://travis-ci.com/libp2p/go-libp2p-pubsub-router) | [](https://codecov.io/gh/libp2p/go-libp2p-pubsub-router) | record-store over pubsub adapter |
|
||||
| **Consensus** |
|
||||
| [`go-libp2p-consensus`](//github.com/libp2p/go-libp2p-consensus) | [](https://travis-ci.com/libp2p/go-libp2p-consensus) | [](https://codecov.io/gh/libp2p/go-libp2p-consensus) | consensus protocols interfaces |
|
||||
| [`go-libp2p-raft`](//github.com/libp2p/go-libp2p-raft) | [](https://travis-ci.com/libp2p/go-libp2p-raft) | [](https://codecov.io/gh/libp2p/go-libp2p-raft) | consensus implementation over raft |
|
||||
| **Pubsub** |
|
||||
| [`go-libp2p-pubsub`](//github.com/libp2p/go-libp2p-pubsub) | [](https://travis-ci.com/libp2p/go-libp2p-pubsub) | [](https://codecov.io/gh/libp2p/go-libp2p-pubsub) | multiple pubsub over libp2p implementations |
|
||||
| **RPC** |
|
||||
| [`go-libp2p-gorpc`](//github.com/libp2p/go-libp2p-gorpc) | [](https://travis-ci.com/libp2p/go-libp2p-gorpc) | [](https://codecov.io/gh/libp2p/go-libp2p-gorpc) | a simple RPC library for libp2p |
|
||||
| **Metrics** |
|
||||
| [`go-libp2p-metrics`](//github.com/libp2p/go-libp2p-metrics) | [](https://travis-ci.com/libp2p/go-libp2p-metrics) | [](https://codecov.io/gh/libp2p/go-libp2p-metrics) | libp2p metrics interfaces/collectors |
|
||||
| **Data Types** |
|
||||
| [`go-libp2p-peer`](//github.com/libp2p/go-libp2p-peer) | [](https://travis-ci.com/libp2p/go-libp2p-peer) | [](https://codecov.io/gh/libp2p/go-libp2p-peer) | libp2p peer-ID datatype |
|
||||
| [`go-libp2p-crypto`](//github.com/libp2p/go-libp2p-crypto) | [](https://travis-ci.com/libp2p/go-libp2p-crypto) | [](https://codecov.io/gh/libp2p/go-libp2p-crypto) | libp2p key types |
|
||||
| [`go-libp2p-protocol`](//github.com/libp2p/go-libp2p-protocol) | [](https://travis-ci.com/libp2p/go-libp2p-protocol) | [](https://codecov.io/gh/libp2p/go-libp2p-protocol) | libp2p protocol datatype |
|
||||
| [`go-libp2p-kbucket`](//github.com/libp2p/go-libp2p-kbucket) | [](https://travis-ci.com/libp2p/go-libp2p-kbucket) | [](https://codecov.io/gh/libp2p/go-libp2p-kbucket) | Kademlia routing table helper types |
|
||||
| **Utilities/miscellaneous** |
|
||||
| [`go-libp2p-loggables`](//github.com/libp2p/go-libp2p-loggables) | [](https://travis-ci.com/libp2p/go-libp2p-loggables) | [](https://codecov.io/gh/libp2p/go-libp2p-loggables) | logging helpers |
|
||||
| [`go-maddr-filter`](//github.com/libp2p/go-maddr-filter) | [](https://travis-ci.com/libp2p/go-maddr-filter) | [](https://codecov.io/gh/libp2p/go-maddr-filter) | multiaddr filtering helpers |
|
||||
| [`go-libp2p-netutil`](//github.com/libp2p/go-libp2p-netutil) | [](https://travis-ci.com/libp2p/go-libp2p-netutil) | [](https://codecov.io/gh/libp2p/go-libp2p-netutil) | misc utilities |
|
||||
| [`go-msgio`](//github.com/libp2p/go-msgio) | [](https://travis-ci.com/libp2p/go-msgio) | [](https://codecov.io/gh/libp2p/go-msgio) | length prefixed data channel |
|
||||
| [`go-addr-util`](//github.com/libp2p/go-addr-util) | [](https://travis-ci.com/libp2p/go-addr-util) | [](https://codecov.io/gh/libp2p/go-addr-util) | address utilities for libp2p swarm |
|
||||
| [`go-buffer-pool`](//github.com/libp2p/go-buffer-pool) | [](https://travis-ci.com/libp2p/go-buffer-pool) | [](https://codecov.io/gh/libp2p/go-buffer-pool) | a variable size buffer pool for go |
|
||||
| [`go-libp2p-routing-helpers`](//github.com/libp2p/go-libp2p-routing-helpers) | [](https://travis-ci.com/libp2p/go-libp2p-routing-helpers) | [](https://codecov.io/gh/libp2p/go-libp2p-routing-helpers) | routing helpers |
|
||||
| [`go-reuseport`](//github.com/libp2p/go-reuseport) | [](https://travis-ci.com/libp2p/go-reuseport) | [](https://codecov.io/gh/libp2p/go-reuseport) | enables reuse of addresses |
|
||||
| [`go-sockaddr`](//github.com/libp2p/go-sockaddr) | [](https://travis-ci.com/libp2p/go-sockaddr) | [](https://codecov.io/gh/libp2p/go-sockaddr) | utils for sockaddr conversions |
|
||||
| [`go-flow-metrics`](//github.com/libp2p/go-flow-metrics) | [](https://travis-ci.com/libp2p/go-flow-metrics) | [](https://codecov.io/gh/libp2p/go-flow-metrics) | metrics library |
|
||||
| **Testing and examples** |
|
||||
| [`go-testutil`](//github.com/libp2p/go-testutil) | [](https://travis-ci.com/libp2p/go-testutil) | [](https://codecov.io/gh/libp2p/go-testutil) | a collection of testing utilities for ipfs and libp2p |
|
||||
| [`go-libp2p-examples`](//github.com/libp2p/go-libp2p-examples) | [](https://travis-ci.com/libp2p/go-libp2p-examples) | [](https://codecov.io/gh/libp2p/go-libp2p-examples) | go-libp2p examples and tutorials |
|
||||
| [`go-libp2p-circuit-progs`](//github.com/libp2p/go-libp2p-circuit-progs) | [](https://travis-ci.com/libp2p/go-libp2p-circuit-progs) | [](https://codecov.io/gh/libp2p/go-libp2p-circuit-progs) | testing programs for go-libp2p-circuit |
|
||||
|
||||
# Contribute
|
||||
|
||||
go-libp2p is part of [The IPFS Project](https://github.com/ipfs/ipfs), and is MIT licensed open source software. We welcome contributions big and small! Take a look at the [community contributing notes](https://github.com/ipfs/community/blob/master/CONTRIBUTING.md). Please make sure to check the [issues](https://github.com/ipfs/go-libp2p/issues). Search the closed ones before reporting things, and help us with the open ones.
|
||||
|
||||
Guidelines:
|
||||
|
||||
- read the [libp2p spec](https://github.com/libp2p/specs)
|
||||
- please make branches + pull-request, even if working on the main repository
|
||||
- ask questions or talk about things in [Issues](https://github.com/libp2p/go-libp2p/issues), our [discussion forums](https://discuss.libp2p.io), or #libp2p or #ipfs on freenode.
|
||||
- ensure you are able to contribute (no legal issues please-- we use the DCO)
|
||||
- run `go fmt` before pushing any code
|
||||
- run `golint` and `go vet` too -- some things (like protobuf files) are expected to fail.
|
||||
- get in touch with @jbenet and @diasdavid about how best to contribute
|
||||
- have fun!
|
||||
|
||||
There's a few things you can do right now to help out:
|
||||
- Go through the modules below and **check out existing issues**. This would be especially useful for modules in active development. Some knowledge of IPFS/libp2p may be required, as well as the infrasture behind it - for instance, you may need to read up on p2p and more complex operations like muxing to be able to help technically.
|
||||
- **Perform code reviews**.
|
||||
- **Add tests**. There can never be enough tests.
|
||||
|
||||
---
|
||||
|
||||
The last gx published version of this module was: 6.0.41: QmTRN7hRxvGkxKxDdeudty7sRet4L7ZKZCqKsXHa79wmAc
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
coverage:
|
||||
range: "50...100"
|
||||
comment: off
|
||||
+256
@@ -0,0 +1,256 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
bhost "github.com/libp2p/go-libp2p/p2p/host/basic"
|
||||
relay "github.com/libp2p/go-libp2p/p2p/host/relay"
|
||||
routed "github.com/libp2p/go-libp2p/p2p/host/routed"
|
||||
|
||||
logging "github.com/ipfs/go-log"
|
||||
circuit "github.com/libp2p/go-libp2p-circuit"
|
||||
crypto "github.com/libp2p/go-libp2p-crypto"
|
||||
discovery "github.com/libp2p/go-libp2p-discovery"
|
||||
host "github.com/libp2p/go-libp2p-host"
|
||||
ifconnmgr "github.com/libp2p/go-libp2p-interface-connmgr"
|
||||
pnet "github.com/libp2p/go-libp2p-interface-pnet"
|
||||
metrics "github.com/libp2p/go-libp2p-metrics"
|
||||
inet "github.com/libp2p/go-libp2p-net"
|
||||
peer "github.com/libp2p/go-libp2p-peer"
|
||||
pstore "github.com/libp2p/go-libp2p-peerstore"
|
||||
routing "github.com/libp2p/go-libp2p-routing"
|
||||
swarm "github.com/libp2p/go-libp2p-swarm"
|
||||
tptu "github.com/libp2p/go-libp2p-transport-upgrader"
|
||||
filter "github.com/libp2p/go-maddr-filter"
|
||||
ma "github.com/multiformats/go-multiaddr"
|
||||
)
|
||||
|
||||
var log = logging.Logger("p2p-config")
|
||||
|
||||
// AddrsFactory is a function that takes a set of multiaddrs we're listening on and
|
||||
// returns the set of multiaddrs we should advertise to the network.
|
||||
type AddrsFactory = bhost.AddrsFactory
|
||||
|
||||
// NATManagerC is a NATManager constructor.
|
||||
type NATManagerC func(inet.Network) bhost.NATManager
|
||||
|
||||
type RoutingC func(host.Host) (routing.PeerRouting, error)
|
||||
|
||||
// Config describes a set of settings for a libp2p node
|
||||
//
|
||||
// This is *not* a stable interface. Use the options defined in the root
|
||||
// package.
|
||||
type Config struct {
|
||||
PeerKey crypto.PrivKey
|
||||
|
||||
Transports []TptC
|
||||
Muxers []MsMuxC
|
||||
SecurityTransports []MsSecC
|
||||
Insecure bool
|
||||
Protector pnet.Protector
|
||||
|
||||
RelayCustom bool
|
||||
Relay bool
|
||||
RelayOpts []circuit.RelayOpt
|
||||
|
||||
ListenAddrs []ma.Multiaddr
|
||||
AddrsFactory bhost.AddrsFactory
|
||||
Filters *filter.Filters
|
||||
|
||||
ConnManager ifconnmgr.ConnManager
|
||||
NATManager NATManagerC
|
||||
Peerstore pstore.Peerstore
|
||||
Reporter metrics.Reporter
|
||||
|
||||
DisablePing bool
|
||||
|
||||
Routing RoutingC
|
||||
|
||||
EnableAutoRelay bool
|
||||
}
|
||||
|
||||
// NewNode constructs a new libp2p Host from the Config.
|
||||
//
|
||||
// This function consumes the config. Do not reuse it (really!).
|
||||
func (cfg *Config) NewNode(ctx context.Context) (host.Host, error) {
|
||||
// Check this early. Prevents us from even *starting* without verifying this.
|
||||
if pnet.ForcePrivateNetwork && cfg.Protector == nil {
|
||||
log.Error("tried to create a libp2p node with no Private" +
|
||||
" Network Protector but usage of Private Networks" +
|
||||
" is forced by the enviroment")
|
||||
// Note: This is *also* checked the upgrader itself so it'll be
|
||||
// enforced even *if* you don't use the libp2p constructor.
|
||||
return nil, pnet.ErrNotInPrivateNetwork
|
||||
}
|
||||
|
||||
if cfg.PeerKey == nil {
|
||||
return nil, fmt.Errorf("no peer key specified")
|
||||
}
|
||||
|
||||
// Obtain Peer ID from public key
|
||||
pid, err := peer.IDFromPublicKey(cfg.PeerKey.GetPublic())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if cfg.Peerstore == nil {
|
||||
return nil, fmt.Errorf("no peerstore specified")
|
||||
}
|
||||
|
||||
if !cfg.Insecure {
|
||||
if err := cfg.Peerstore.AddPrivKey(pid, cfg.PeerKey); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := cfg.Peerstore.AddPubKey(pid, cfg.PeerKey.GetPublic()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Make the swarm implementation configurable.
|
||||
swrm := swarm.NewSwarm(ctx, pid, cfg.Peerstore, cfg.Reporter)
|
||||
if cfg.Filters != nil {
|
||||
swrm.Filters = cfg.Filters
|
||||
}
|
||||
|
||||
h, err := bhost.NewHost(ctx, swrm, &bhost.HostOpts{
|
||||
ConnManager: cfg.ConnManager,
|
||||
AddrsFactory: cfg.AddrsFactory,
|
||||
NATManager: cfg.NATManager,
|
||||
EnablePing: !cfg.DisablePing,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
swrm.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if cfg.Relay {
|
||||
// If we've enabled the relay, we should filter out relay
|
||||
// addresses by default.
|
||||
//
|
||||
// TODO: We shouldn't be doing this here.
|
||||
oldFactory := h.AddrsFactory
|
||||
h.AddrsFactory = func(addrs []ma.Multiaddr) []ma.Multiaddr {
|
||||
return oldFactory(relay.Filter(addrs))
|
||||
}
|
||||
}
|
||||
|
||||
upgrader := new(tptu.Upgrader)
|
||||
upgrader.Protector = cfg.Protector
|
||||
upgrader.Filters = swrm.Filters
|
||||
if cfg.Insecure {
|
||||
upgrader.Secure = makeInsecureTransport(pid)
|
||||
} else {
|
||||
upgrader.Secure, err = makeSecurityTransport(h, cfg.SecurityTransports)
|
||||
if err != nil {
|
||||
h.Close()
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
upgrader.Muxer, err = makeMuxer(h, cfg.Muxers)
|
||||
if err != nil {
|
||||
h.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tpts, err := makeTransports(h, upgrader, cfg.Transports)
|
||||
if err != nil {
|
||||
h.Close()
|
||||
return nil, err
|
||||
}
|
||||
for _, t := range tpts {
|
||||
err = swrm.AddTransport(t)
|
||||
if err != nil {
|
||||
h.Close()
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if cfg.Relay {
|
||||
err := circuit.AddRelayTransport(swrm.Context(), h, upgrader, cfg.RelayOpts...)
|
||||
if err != nil {
|
||||
h.Close()
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: This method succeeds if listening on one address succeeds. We
|
||||
// should probably fail if listening on *any* addr fails.
|
||||
if err := h.Network().Listen(cfg.ListenAddrs...); err != nil {
|
||||
h.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Configure routing and autorelay
|
||||
var router routing.PeerRouting
|
||||
if cfg.Routing != nil {
|
||||
router, err = cfg.Routing(h)
|
||||
if err != nil {
|
||||
h.Close()
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if cfg.EnableAutoRelay {
|
||||
if !cfg.Relay {
|
||||
h.Close()
|
||||
return nil, fmt.Errorf("cannot enable autorelay; relay is not enabled")
|
||||
}
|
||||
|
||||
if router == nil {
|
||||
h.Close()
|
||||
return nil, fmt.Errorf("cannot enable autorelay; no routing for discovery")
|
||||
}
|
||||
|
||||
crouter, ok := router.(routing.ContentRouting)
|
||||
if !ok {
|
||||
h.Close()
|
||||
return nil, fmt.Errorf("cannot enable autorelay; no suitable routing for discovery")
|
||||
}
|
||||
|
||||
discovery := discovery.NewRoutingDiscovery(crouter)
|
||||
|
||||
hop := false
|
||||
for _, opt := range cfg.RelayOpts {
|
||||
if opt == circuit.OptHop {
|
||||
hop = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if hop {
|
||||
// advertise ourselves
|
||||
relay.Advertise(ctx, discovery)
|
||||
} else {
|
||||
_ = relay.NewAutoRelay(swrm.Context(), h, discovery, router)
|
||||
}
|
||||
}
|
||||
|
||||
// start the host background tasks
|
||||
h.Start()
|
||||
|
||||
if router != nil {
|
||||
return routed.Wrap(h, router), nil
|
||||
}
|
||||
return h, nil
|
||||
}
|
||||
|
||||
// Option is a libp2p config option that can be given to the libp2p constructor
|
||||
// (`libp2p.New`).
|
||||
type Option func(cfg *Config) error
|
||||
|
||||
// Apply applies the given options to the config, returning the first error
|
||||
// encountered (if any).
|
||||
func (cfg *Config) Apply(opts ...Option) error {
|
||||
for _, opt := range opts {
|
||||
if opt == nil {
|
||||
continue
|
||||
}
|
||||
if err := opt(cfg); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
security "github.com/libp2p/go-conn-security"
|
||||
crypto "github.com/libp2p/go-libp2p-crypto"
|
||||
host "github.com/libp2p/go-libp2p-host"
|
||||
pnet "github.com/libp2p/go-libp2p-interface-pnet"
|
||||
inet "github.com/libp2p/go-libp2p-net"
|
||||
peer "github.com/libp2p/go-libp2p-peer"
|
||||
pstore "github.com/libp2p/go-libp2p-peerstore"
|
||||
transport "github.com/libp2p/go-libp2p-transport"
|
||||
tptu "github.com/libp2p/go-libp2p-transport-upgrader"
|
||||
filter "github.com/libp2p/go-maddr-filter"
|
||||
mux "github.com/libp2p/go-stream-muxer"
|
||||
)
|
||||
|
||||
var (
|
||||
// interfaces
|
||||
hostType = reflect.TypeOf((*host.Host)(nil)).Elem()
|
||||
networkType = reflect.TypeOf((*inet.Network)(nil)).Elem()
|
||||
transportType = reflect.TypeOf((*transport.Transport)(nil)).Elem()
|
||||
muxType = reflect.TypeOf((*mux.Transport)(nil)).Elem()
|
||||
securityType = reflect.TypeOf((*security.Transport)(nil)).Elem()
|
||||
protectorType = reflect.TypeOf((*pnet.Protector)(nil)).Elem()
|
||||
privKeyType = reflect.TypeOf((*crypto.PrivKey)(nil)).Elem()
|
||||
pubKeyType = reflect.TypeOf((*crypto.PubKey)(nil)).Elem()
|
||||
pstoreType = reflect.TypeOf((*pstore.Peerstore)(nil)).Elem()
|
||||
|
||||
// concrete types
|
||||
peerIDType = reflect.TypeOf((peer.ID)(""))
|
||||
filtersType = reflect.TypeOf((*filter.Filters)(nil))
|
||||
upgraderType = reflect.TypeOf((*tptu.Upgrader)(nil))
|
||||
)
|
||||
|
||||
var argTypes = map[reflect.Type]constructor{
|
||||
upgraderType: func(h host.Host, u *tptu.Upgrader) interface{} { return u },
|
||||
hostType: func(h host.Host, u *tptu.Upgrader) interface{} { return h },
|
||||
networkType: func(h host.Host, u *tptu.Upgrader) interface{} { return h.Network() },
|
||||
muxType: func(h host.Host, u *tptu.Upgrader) interface{} { return u.Muxer },
|
||||
securityType: func(h host.Host, u *tptu.Upgrader) interface{} { return u.Secure },
|
||||
protectorType: func(h host.Host, u *tptu.Upgrader) interface{} { return u.Protector },
|
||||
filtersType: func(h host.Host, u *tptu.Upgrader) interface{} { return u.Filters },
|
||||
peerIDType: func(h host.Host, u *tptu.Upgrader) interface{} { return h.ID() },
|
||||
privKeyType: func(h host.Host, u *tptu.Upgrader) interface{} { return h.Peerstore().PrivKey(h.ID()) },
|
||||
pubKeyType: func(h host.Host, u *tptu.Upgrader) interface{} { return h.Peerstore().PubKey(h.ID()) },
|
||||
pstoreType: func(h host.Host, u *tptu.Upgrader) interface{} { return h.Peerstore() },
|
||||
}
|
||||
|
||||
func newArgTypeSet(types ...reflect.Type) map[reflect.Type]constructor {
|
||||
result := make(map[reflect.Type]constructor, len(types))
|
||||
for _, ty := range types {
|
||||
c, ok := argTypes[ty]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("missing constructor for type %s", ty))
|
||||
}
|
||||
result[ty] = c
|
||||
}
|
||||
return result
|
||||
}
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
host "github.com/libp2p/go-libp2p-host"
|
||||
mux "github.com/libp2p/go-stream-muxer"
|
||||
msmux "github.com/whyrusleeping/go-smux-multistream"
|
||||
)
|
||||
|
||||
// MuxC is a stream multiplex transport constructor
|
||||
type MuxC func(h host.Host) (mux.Transport, error)
|
||||
|
||||
// MsMuxC is a tuple containing a multiplex transport constructor and a protocol
|
||||
// ID.
|
||||
type MsMuxC struct {
|
||||
MuxC
|
||||
ID string
|
||||
}
|
||||
|
||||
var muxArgTypes = newArgTypeSet(hostType, networkType, peerIDType, pstoreType)
|
||||
|
||||
// MuxerConstructor creates a multiplex constructor from the passed parameter
|
||||
// using reflection.
|
||||
func MuxerConstructor(m interface{}) (MuxC, error) {
|
||||
// Already constructed?
|
||||
if t, ok := m.(mux.Transport); ok {
|
||||
return func(_ host.Host) (mux.Transport, error) {
|
||||
return t, nil
|
||||
}, nil
|
||||
}
|
||||
|
||||
ctor, err := makeConstructor(m, muxType, muxArgTypes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return func(h host.Host) (mux.Transport, error) {
|
||||
t, err := ctor(h, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return t.(mux.Transport), nil
|
||||
}, nil
|
||||
}
|
||||
|
||||
func makeMuxer(h host.Host, tpts []MsMuxC) (mux.Transport, error) {
|
||||
muxMuxer := msmux.NewBlankTransport()
|
||||
transportSet := make(map[string]struct{}, len(tpts))
|
||||
for _, tptC := range tpts {
|
||||
if _, ok := transportSet[tptC.ID]; ok {
|
||||
return nil, fmt.Errorf("duplicate muxer transport: %s", tptC.ID)
|
||||
}
|
||||
transportSet[tptC.ID] = struct{}{}
|
||||
}
|
||||
for _, tptC := range tpts {
|
||||
tpt, err := tptC.MuxC(h)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
muxMuxer.AddTransport(tptC.ID, tpt)
|
||||
}
|
||||
return muxMuxer, nil
|
||||
}
|
||||
+126
@@ -0,0 +1,126 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"runtime"
|
||||
|
||||
host "github.com/libp2p/go-libp2p-host"
|
||||
tptu "github.com/libp2p/go-libp2p-transport-upgrader"
|
||||
)
|
||||
|
||||
var errorType = reflect.TypeOf((*error)(nil)).Elem()
|
||||
|
||||
// checks if a function returns either the specified type or the specified type
|
||||
// and an error.
|
||||
func checkReturnType(fnType, tptType reflect.Type) error {
|
||||
switch fnType.NumOut() {
|
||||
case 2:
|
||||
if fnType.Out(1) != errorType {
|
||||
return fmt.Errorf("expected (optional) second return value from transport constructor to be an error")
|
||||
}
|
||||
|
||||
fallthrough
|
||||
case 1:
|
||||
if !fnType.Out(0).Implements(tptType) {
|
||||
return fmt.Errorf("transport constructor returns %s which doesn't implement %s", fnType.Out(0), tptType)
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("expected transport constructor to return a transport and, optionally, an error")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Handles return values with optional errors. That is, return values of the
|
||||
// form `(something, error)` or just `something`.
|
||||
//
|
||||
// Panics if the return value isn't of the correct form.
|
||||
func handleReturnValue(out []reflect.Value) (interface{}, error) {
|
||||
switch len(out) {
|
||||
case 2:
|
||||
err := out[1]
|
||||
if err != (reflect.Value{}) && !err.IsNil() {
|
||||
return nil, err.Interface().(error)
|
||||
}
|
||||
fallthrough
|
||||
case 1:
|
||||
tpt := out[0]
|
||||
|
||||
// Check for nil value and nil error.
|
||||
if tpt == (reflect.Value{}) {
|
||||
return nil, fmt.Errorf("unspecified error")
|
||||
}
|
||||
switch tpt.Kind() {
|
||||
case reflect.Ptr, reflect.Interface, reflect.Func:
|
||||
if tpt.IsNil() {
|
||||
return nil, fmt.Errorf("unspecified error")
|
||||
}
|
||||
}
|
||||
|
||||
return tpt.Interface(), nil
|
||||
default:
|
||||
panic("expected 1 or 2 return values from transport constructor")
|
||||
}
|
||||
}
|
||||
|
||||
// calls the transport constructor and annotates the error with the name of the constructor.
|
||||
func callConstructor(c reflect.Value, args []reflect.Value) (interface{}, error) {
|
||||
val, err := handleReturnValue(c.Call(args))
|
||||
if err != nil {
|
||||
name := runtime.FuncForPC(c.Pointer()).Name()
|
||||
if name != "" {
|
||||
// makes debugging easier
|
||||
return nil, fmt.Errorf("transport constructor %s failed: %s", name, err)
|
||||
}
|
||||
}
|
||||
return val, err
|
||||
}
|
||||
|
||||
type constructor func(h host.Host, u *tptu.Upgrader) interface{}
|
||||
|
||||
func makeArgumentConstructors(fnType reflect.Type, argTypes map[reflect.Type]constructor) ([]constructor, error) {
|
||||
out := make([]constructor, fnType.NumIn())
|
||||
for i := range out {
|
||||
argType := fnType.In(i)
|
||||
c, ok := argTypes[argType]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("argument %d has an unexpected type %s", i, argType.Name())
|
||||
}
|
||||
out[i] = c
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// makes a transport constructor.
|
||||
func makeConstructor(
|
||||
tpt interface{},
|
||||
tptType reflect.Type,
|
||||
argTypes map[reflect.Type]constructor,
|
||||
) (func(host.Host, *tptu.Upgrader) (interface{}, error), error) {
|
||||
v := reflect.ValueOf(tpt)
|
||||
// avoid panicing on nil/zero value.
|
||||
if v == (reflect.Value{}) {
|
||||
return nil, fmt.Errorf("expected a transport or transport constructor, got a %T", tpt)
|
||||
}
|
||||
t := v.Type()
|
||||
if t.Kind() != reflect.Func {
|
||||
return nil, fmt.Errorf("expected a transport or transport constructor, got a %T", tpt)
|
||||
}
|
||||
|
||||
if err := checkReturnType(t, tptType); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
argConstructors, err := makeArgumentConstructors(t, argTypes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return func(h host.Host, u *tptu.Upgrader) (interface{}, error) {
|
||||
arguments := make([]reflect.Value, len(argConstructors))
|
||||
for i, makeArg := range argConstructors {
|
||||
arguments[i] = reflect.ValueOf(makeArg(h, u))
|
||||
}
|
||||
return callConstructor(v, arguments)
|
||||
}, nil
|
||||
}
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
security "github.com/libp2p/go-conn-security"
|
||||
csms "github.com/libp2p/go-conn-security-multistream"
|
||||
insecure "github.com/libp2p/go-conn-security/insecure"
|
||||
host "github.com/libp2p/go-libp2p-host"
|
||||
peer "github.com/libp2p/go-libp2p-peer"
|
||||
)
|
||||
|
||||
// SecC is a security transport constructor
|
||||
type SecC func(h host.Host) (security.Transport, error)
|
||||
|
||||
// MsSecC is a tuple containing a security transport constructor and a protocol
|
||||
// ID.
|
||||
type MsSecC struct {
|
||||
SecC
|
||||
ID string
|
||||
}
|
||||
|
||||
var securityArgTypes = newArgTypeSet(
|
||||
hostType, networkType, peerIDType,
|
||||
privKeyType, pubKeyType, pstoreType,
|
||||
)
|
||||
|
||||
// SecurityConstructor creates a security constructor from the passed parameter
|
||||
// using reflection.
|
||||
func SecurityConstructor(sec interface{}) (SecC, error) {
|
||||
// Already constructed?
|
||||
if t, ok := sec.(security.Transport); ok {
|
||||
return func(_ host.Host) (security.Transport, error) {
|
||||
return t, nil
|
||||
}, nil
|
||||
}
|
||||
|
||||
ctor, err := makeConstructor(sec, securityType, securityArgTypes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return func(h host.Host) (security.Transport, error) {
|
||||
t, err := ctor(h, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return t.(security.Transport), nil
|
||||
}, nil
|
||||
}
|
||||
|
||||
func makeInsecureTransport(id peer.ID) security.Transport {
|
||||
secMuxer := new(csms.SSMuxer)
|
||||
secMuxer.AddTransport(insecure.ID, insecure.New(id))
|
||||
return secMuxer
|
||||
}
|
||||
|
||||
func makeSecurityTransport(h host.Host, tpts []MsSecC) (security.Transport, error) {
|
||||
secMuxer := new(csms.SSMuxer)
|
||||
transportSet := make(map[string]struct{}, len(tpts))
|
||||
for _, tptC := range tpts {
|
||||
if _, ok := transportSet[tptC.ID]; ok {
|
||||
return nil, fmt.Errorf("duplicate security transport: %s", tptC.ID)
|
||||
}
|
||||
transportSet[tptC.ID] = struct{}{}
|
||||
}
|
||||
for _, tptC := range tpts {
|
||||
tpt, err := tptC.SecC(h)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, ok := tpt.(*insecure.Transport); ok {
|
||||
return nil, fmt.Errorf("cannot construct libp2p with an insecure transport, set the Insecure config option instead")
|
||||
}
|
||||
secMuxer.AddTransport(tptC.ID, tpt)
|
||||
}
|
||||
return secMuxer, nil
|
||||
}
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
host "github.com/libp2p/go-libp2p-host"
|
||||
transport "github.com/libp2p/go-libp2p-transport"
|
||||
tptu "github.com/libp2p/go-libp2p-transport-upgrader"
|
||||
)
|
||||
|
||||
// TptC is the type for libp2p transport constructors. You probably won't ever
|
||||
// implement this function interface directly. Instead, pass your transport
|
||||
// constructor to TransportConstructor.
|
||||
type TptC func(h host.Host, u *tptu.Upgrader) (transport.Transport, error)
|
||||
|
||||
var transportArgTypes = argTypes
|
||||
|
||||
// TransportConstructor uses reflection to turn a function that constructs a
|
||||
// transport into a TptC.
|
||||
//
|
||||
// You can pass either a constructed transport (something that implements
|
||||
// `transport.Transport`) or a function that takes any of:
|
||||
//
|
||||
// * The local peer ID.
|
||||
// * A transport connection upgrader.
|
||||
// * A private key.
|
||||
// * A public key.
|
||||
// * A Host.
|
||||
// * A Network.
|
||||
// * A Peerstore.
|
||||
// * An address filter.
|
||||
// * A security transport.
|
||||
// * A stream multiplexer transport.
|
||||
// * A private network protector.
|
||||
//
|
||||
// And returns a type implementing transport.Transport and, optionally, an error
|
||||
// (as the second argument).
|
||||
func TransportConstructor(tpt interface{}) (TptC, error) {
|
||||
// Already constructed?
|
||||
if t, ok := tpt.(transport.Transport); ok {
|
||||
return func(_ host.Host, _ *tptu.Upgrader) (transport.Transport, error) {
|
||||
return t, nil
|
||||
}, nil
|
||||
}
|
||||
ctor, err := makeConstructor(tpt, transportType, transportArgTypes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return func(h host.Host, u *tptu.Upgrader) (transport.Transport, error) {
|
||||
t, err := ctor(h, u)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return t.(transport.Transport), nil
|
||||
}, nil
|
||||
}
|
||||
|
||||
func makeTransports(h host.Host, u *tptu.Upgrader, tpts []TptC) ([]transport.Transport, error) {
|
||||
transports := make([]transport.Transport, len(tpts))
|
||||
for i, tC := range tpts {
|
||||
t, err := tC(h, u)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
transports[i] = t
|
||||
}
|
||||
return transports, nil
|
||||
}
|
||||
+140
@@ -0,0 +1,140 @@
|
||||
package libp2p
|
||||
|
||||
// This file contains all the default configuration options.
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
|
||||
crypto "github.com/libp2p/go-libp2p-crypto"
|
||||
pstoremem "github.com/libp2p/go-libp2p-peerstore/pstoremem"
|
||||
secio "github.com/libp2p/go-libp2p-secio"
|
||||
tcp "github.com/libp2p/go-tcp-transport"
|
||||
ws "github.com/libp2p/go-ws-transport"
|
||||
multiaddr "github.com/multiformats/go-multiaddr"
|
||||
mplex "github.com/whyrusleeping/go-smux-multiplex"
|
||||
yamux "github.com/whyrusleeping/go-smux-yamux"
|
||||
)
|
||||
|
||||
// DefaultSecurity is the default security option.
|
||||
//
|
||||
// Useful when you want to extend, but not replace, the supported transport
|
||||
// security protocols.
|
||||
var DefaultSecurity = Security(secio.ID, secio.New)
|
||||
|
||||
// DefaultMuxers configures libp2p to use the stream connection multiplexers.
|
||||
//
|
||||
// Use this option when you want to *extend* the set of multiplexers used by
|
||||
// libp2p instead of replacing them.
|
||||
var DefaultMuxers = ChainOptions(
|
||||
Muxer("/yamux/1.0.0", yamux.DefaultTransport),
|
||||
Muxer("/mplex/6.7.0", mplex.DefaultTransport),
|
||||
)
|
||||
|
||||
// DefaultTransports are the default libp2p transports.
|
||||
//
|
||||
// Use this option when you want to *extend* the set of transports used by
|
||||
// libp2p instead of replacing them.
|
||||
var DefaultTransports = ChainOptions(
|
||||
Transport(tcp.NewTCPTransport),
|
||||
Transport(ws.New),
|
||||
)
|
||||
|
||||
// DefaultPeerstore configures libp2p to use the default peerstore.
|
||||
var DefaultPeerstore Option = func(cfg *Config) error {
|
||||
return cfg.Apply(Peerstore(pstoremem.NewPeerstore()))
|
||||
}
|
||||
|
||||
// RandomIdentity generates a random identity (default behaviour)
|
||||
var RandomIdentity = func(cfg *Config) error {
|
||||
priv, _, err := crypto.GenerateKeyPairWithReader(crypto.RSA, 2048, rand.Reader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return cfg.Apply(Identity(priv))
|
||||
}
|
||||
|
||||
// DefaultListenAddrs configures libp2p to use default listen address
|
||||
var DefaultListenAddrs = func(cfg *Config) error {
|
||||
defaultIP4ListenAddr, err := multiaddr.NewMultiaddr("/ip4/0.0.0.0/tcp/0")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defaultIP6ListenAddr, err := multiaddr.NewMultiaddr("/ip6/::/tcp/0")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return cfg.Apply(ListenAddrs(
|
||||
defaultIP4ListenAddr,
|
||||
defaultIP6ListenAddr,
|
||||
))
|
||||
}
|
||||
|
||||
// DefaultEnableRelay enables relay dialing and listening by default
|
||||
var DefaultEnableRelay = func(cfg *Config) error {
|
||||
return cfg.Apply(EnableRelay())
|
||||
}
|
||||
|
||||
// Complete list of default options and when to fallback on them.
|
||||
//
|
||||
// Please *DON'T* specify default options any other way. Putting this all here
|
||||
// makes tracking defaults *much* easier.
|
||||
var defaults = []struct {
|
||||
fallback func(cfg *Config) bool
|
||||
opt Option
|
||||
}{
|
||||
{
|
||||
fallback: func(cfg *Config) bool { return cfg.Transports == nil && cfg.ListenAddrs == nil },
|
||||
opt: DefaultListenAddrs,
|
||||
},
|
||||
{
|
||||
fallback: func(cfg *Config) bool { return cfg.Transports == nil },
|
||||
opt: DefaultTransports,
|
||||
},
|
||||
{
|
||||
fallback: func(cfg *Config) bool { return cfg.Muxers == nil },
|
||||
opt: DefaultMuxers,
|
||||
},
|
||||
{
|
||||
fallback: func(cfg *Config) bool { return !cfg.Insecure && cfg.SecurityTransports == nil },
|
||||
opt: DefaultSecurity,
|
||||
},
|
||||
{
|
||||
fallback: func(cfg *Config) bool { return cfg.PeerKey == nil },
|
||||
opt: RandomIdentity,
|
||||
},
|
||||
{
|
||||
fallback: func(cfg *Config) bool { return cfg.Peerstore == nil },
|
||||
opt: DefaultPeerstore,
|
||||
},
|
||||
{
|
||||
fallback: func(cfg *Config) bool { return !cfg.RelayCustom },
|
||||
opt: DefaultEnableRelay,
|
||||
},
|
||||
}
|
||||
|
||||
// Defaults configures libp2p to use the default options. Can be combined with
|
||||
// other options to *extend* the default options.
|
||||
var Defaults Option = func(cfg *Config) error {
|
||||
for _, def := range defaults {
|
||||
if err := cfg.Apply(def.opt); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// FallbackDefaults applies default options to the libp2p node if and only if no
|
||||
// other relevent options have been applied. will be appended to the options
|
||||
// passed into New.
|
||||
var FallbackDefaults Option = func(cfg *Config) error {
|
||||
for _, def := range defaults {
|
||||
if !def.fallback(cfg) {
|
||||
continue
|
||||
}
|
||||
if err := cfg.Apply(def.opt); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package libp2p
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
func traceError(err error, skip int) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
_, file, line, ok := runtime.Caller(skip + 1)
|
||||
if !ok {
|
||||
return err
|
||||
}
|
||||
return fmt.Errorf("%s:%d: %s", file, line, err)
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
module github.com/libp2p/go-libp2p
|
||||
|
||||
require (
|
||||
github.com/gogo/protobuf v1.2.1
|
||||
github.com/ipfs/go-cid v0.0.1
|
||||
github.com/ipfs/go-detect-race v0.0.1
|
||||
github.com/ipfs/go-ipfs-util v0.0.1
|
||||
github.com/ipfs/go-log v0.0.1
|
||||
github.com/jbenet/goprocess v0.0.0-20160826012719-b497e2f366b8
|
||||
github.com/libp2p/go-conn-security v0.0.1
|
||||
github.com/libp2p/go-conn-security-multistream v0.0.1
|
||||
github.com/libp2p/go-libp2p-autonat v0.0.4
|
||||
github.com/libp2p/go-libp2p-blankhost v0.0.1
|
||||
github.com/libp2p/go-libp2p-circuit v0.0.4
|
||||
github.com/libp2p/go-libp2p-crypto v0.0.1
|
||||
github.com/libp2p/go-libp2p-discovery v0.0.2
|
||||
github.com/libp2p/go-libp2p-host v0.0.1
|
||||
github.com/libp2p/go-libp2p-interface-connmgr v0.0.1
|
||||
github.com/libp2p/go-libp2p-interface-pnet v0.0.1
|
||||
github.com/libp2p/go-libp2p-loggables v0.0.1
|
||||
github.com/libp2p/go-libp2p-metrics v0.0.1
|
||||
github.com/libp2p/go-libp2p-nat v0.0.4
|
||||
github.com/libp2p/go-libp2p-net v0.0.2
|
||||
github.com/libp2p/go-libp2p-netutil v0.0.1
|
||||
github.com/libp2p/go-libp2p-peer v0.0.1
|
||||
github.com/libp2p/go-libp2p-peerstore v0.0.2
|
||||
github.com/libp2p/go-libp2p-protocol v0.0.1
|
||||
github.com/libp2p/go-libp2p-routing v0.0.1
|
||||
github.com/libp2p/go-libp2p-secio v0.0.2
|
||||
github.com/libp2p/go-libp2p-swarm v0.0.2
|
||||
github.com/libp2p/go-libp2p-transport v0.0.4
|
||||
github.com/libp2p/go-libp2p-transport-upgrader v0.0.1
|
||||
github.com/libp2p/go-maddr-filter v0.0.1
|
||||
github.com/libp2p/go-stream-muxer v0.0.1
|
||||
github.com/libp2p/go-tcp-transport v0.0.2
|
||||
github.com/libp2p/go-testutil v0.0.1
|
||||
github.com/libp2p/go-ws-transport v0.0.2
|
||||
github.com/miekg/dns v1.1.4 // indirect
|
||||
github.com/multiformats/go-multiaddr v0.0.1
|
||||
github.com/multiformats/go-multiaddr-dns v0.0.2
|
||||
github.com/multiformats/go-multiaddr-net v0.0.1
|
||||
github.com/multiformats/go-multistream v0.0.1
|
||||
github.com/whyrusleeping/go-smux-multiplex v3.0.16+incompatible
|
||||
github.com/whyrusleeping/go-smux-multistream v2.0.2+incompatible
|
||||
github.com/whyrusleeping/go-smux-yamux v2.0.9+incompatible
|
||||
github.com/whyrusleeping/mdns v0.0.0-20180901202407-ef14215e6b30
|
||||
)
|
||||
+261
@@ -0,0 +1,261 @@
|
||||
github.com/AndreasBriese/bbloom v0.0.0-20180913140656-343706a395b7/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
|
||||
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/btcsuite/btcd v0.0.0-20190213025234-306aecffea32 h1:qkOC5Gd33k54tobS36cXdAzJbeHaduLtnLQQwNoIi78=
|
||||
github.com/btcsuite/btcd v0.0.0-20190213025234-306aecffea32/go.mod h1:DrZx5ec/dmnfpw9KyYoQyYo7d0KEvTkk/5M/vbZjAr8=
|
||||
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/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/coreos/go-semver v0.2.0 h1:3Jm3tLmsgAYcjC+4Up7hJrFBPr+n7rAqYeSw/SZazuY=
|
||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
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 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgraph-io/badger v1.5.5-0.20190226225317-8115aed38f8f/go.mod h1:VZxzAIRPHRVNRKRo6AXrX9BJegn6il06VMTZVJYCIjQ=
|
||||
github.com/dgryski/go-farm v0.0.0-20190104051053-3adb47b1fb0f/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||
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/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.0 h1:kbxbvI4Un1LUWKxufD+BiE6AEExYYgkQLQmLFqA1LFk=
|
||||
github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0=
|
||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
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 h1:wrk3uMNaMxbXiHibbPO4S0ymqJMm41WiudyFSs7UnsU=
|
||||
github.com/gxed/hashland/keccakpg v0.0.1/go.mod h1:kRzw3HkwxFU1mpmPP8v1WyQzwdGfmKFJ6tItnhQ67kU=
|
||||
github.com/gxed/hashland/murmur3 v0.0.1 h1:SheiaIt0sda5K+8FLz952/1iWS9zrnKsEJaOJu4ZbSc=
|
||||
github.com/gxed/hashland/murmur3 v0.0.1/go.mod h1:KjXop02n4/ckmZSnY2+HKcLud/tcmvhST0bie/0lS48=
|
||||
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o=
|
||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||
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/ipfs/go-cid v0.0.1 h1:GBjWPktLnNyX0JiQCNFpUuUSoMw5KMyqrsejHYlILBE=
|
||||
github.com/ipfs/go-cid v0.0.1/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 h1:qX/xay2W3E4Q1U7d9lNs1sU9nvguX0a7319XbyQ6cOk=
|
||||
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-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 h1:DQqZhhDvrTrEQ3Qod5yfavcA064e53xlQ+xajiorXgM=
|
||||
github.com/jbenet/go-cienv v0.0.0-20150120210510-1bb1476777ec/go.mod h1:rGaEvXB4uRSZMmzKNLoXvTu1sfx+1kv/DojUlPrSZGs=
|
||||
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 h1:bspPhN+oKYFk5fcGNuQzp6IGzYQSenLEgH3s6jkXrWw=
|
||||
github.com/jbenet/goprocess v0.0.0-20160826012719-b497e2f366b8/go.mod h1:Ly/wlsjFq/qrU3Rar62tu1gASgGw6chQbSh/XgIIXCY=
|
||||
github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ=
|
||||
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/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 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
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 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
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 h1:9Rrn/H46cXjaA2HQ5Y8lyhOS1NhTkZ4yuEs2r3Eechg=
|
||||
github.com/libp2p/go-buffer-pool v0.0.1/go.mod h1:xtyIz9PMobb13WaxR6Zo1Pd1zXJKYg0a8KiIvDp3TzQ=
|
||||
github.com/libp2p/go-conn-security v0.0.1 h1:4kMMrqrt9EUNCNjX1xagSJC+bq16uqjMe9lk1KBMVNs=
|
||||
github.com/libp2p/go-conn-security v0.0.1/go.mod h1:bGmu51N0KU9IEjX7kl2PQjgZa40JQWnayTvNMgD/vyk=
|
||||
github.com/libp2p/go-conn-security-multistream v0.0.1 h1:XefjAQRHcnUaxKb26RGupToucx3uU4ecbOZ3aACXlDU=
|
||||
github.com/libp2p/go-conn-security-multistream v0.0.1/go.mod h1:nc9vud7inQ+d6SO0I/6dSWrdMnHnzZNHeyUQqrAJulE=
|
||||
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-autonat v0.0.4 h1:cZzdB9KW1ZkHnSjLCB6aFNw47XS4r+SecCVMuVB1xgo=
|
||||
github.com/libp2p/go-libp2p-autonat v0.0.4/go.mod h1:fs71q5Xk+pdnKU014o2iq1RhMs9/PMaG5zXRFNnIIT4=
|
||||
github.com/libp2p/go-libp2p-blankhost v0.0.1 h1:/mZuuiwntNR8RywnCFlGHLKrKLYne+qciBpQXWqp5fk=
|
||||
github.com/libp2p/go-libp2p-blankhost v0.0.1/go.mod h1:Ibpbw/7cPPYwFb7PACIWdvxxv0t0XCCI10t7czjAjTc=
|
||||
github.com/libp2p/go-libp2p-circuit v0.0.4 h1:yOgEadnSVFj3e9KLBuLG+edqCImeav0VXxXvcimpOUQ=
|
||||
github.com/libp2p/go-libp2p-circuit v0.0.4/go.mod h1:p1cHJnB9xnX5/1vZLkXgKwmNEOQQuF/Hp+SkATXnXYk=
|
||||
github.com/libp2p/go-libp2p-crypto v0.0.1 h1:JNQd8CmoGTohO/akqrH16ewsqZpci2CbgYH/LmYl8gw=
|
||||
github.com/libp2p/go-libp2p-crypto v0.0.1/go.mod h1:yJkNyDmO341d5wwXxDUGO0LykUVT72ImHNUqh5D/dBE=
|
||||
github.com/libp2p/go-libp2p-discovery v0.0.2 h1:Rf+20nsFcCnHo4Kxvf8ofAft75+fW+cXy9FonNVyU/g=
|
||||
github.com/libp2p/go-libp2p-discovery v0.0.2/go.mod h1:ZkkF9xIFRLA1xCc7bstYFkd80gBGK8Fc1JqGoU2i+zI=
|
||||
github.com/libp2p/go-libp2p-host v0.0.1 h1:dnqusU+DheGcdxrE718kG4XgHNuL2n9eEv8Rg5zy8hQ=
|
||||
github.com/libp2p/go-libp2p-host v0.0.1/go.mod h1:qWd+H1yuU0m5CwzAkvbSjqKairayEHdR5MMl7Cwa7Go=
|
||||
github.com/libp2p/go-libp2p-interface-connmgr v0.0.1 h1:Q9EkNSLAOF+u90L88qmE9z/fTdjLh8OsJwGw74mkwk4=
|
||||
github.com/libp2p/go-libp2p-interface-connmgr v0.0.1/go.mod h1:GarlRLH0LdeWcLnYM/SaBykKFl9U5JFnbBGruAk/D5k=
|
||||
github.com/libp2p/go-libp2p-interface-pnet v0.0.1 h1:7GnzRrBTJHEsofi1ahFdPN9Si6skwXQE9UqR2S+Pkh8=
|
||||
github.com/libp2p/go-libp2p-interface-pnet v0.0.1/go.mod h1:el9jHpQAXK5dnTpKA4yfCNBZXvrzdOU75zz+C6ryp3k=
|
||||
github.com/libp2p/go-libp2p-loggables v0.0.1 h1:HVww9oAnINIxbt69LJNkxD8lnbfgteXR97Xm4p3l9ps=
|
||||
github.com/libp2p/go-libp2p-loggables v0.0.1/go.mod h1:lDipDlBNYbpyqyPX/KcoO+eq0sJYEVR2JgOexcivchg=
|
||||
github.com/libp2p/go-libp2p-metrics v0.0.1 h1:yumdPC/P2VzINdmcKZd0pciSUCpou+s0lwYCjBbzQZU=
|
||||
github.com/libp2p/go-libp2p-metrics v0.0.1/go.mod h1:jQJ95SXXA/K1VZi13h52WZMa9ja78zjyy5rspMsC/08=
|
||||
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-net v0.0.1 h1:xJ4Vh4yKF/XKb8fd1Ev0ebAGzVjMxXzrxG2kjtU+F5Q=
|
||||
github.com/libp2p/go-libp2p-net v0.0.1/go.mod h1:Yt3zgmlsHOgUWSXmt5V/Jpz9upuJBE8EgNU9DrCcR8c=
|
||||
github.com/libp2p/go-libp2p-net v0.0.2 h1:qP06u4TYXfl7uW/hzqPhlVVTSA2nw1B/bHBJaUnbh6M=
|
||||
github.com/libp2p/go-libp2p-net v0.0.2/go.mod h1:Yt3zgmlsHOgUWSXmt5V/Jpz9upuJBE8EgNU9DrCcR8c=
|
||||
github.com/libp2p/go-libp2p-netutil v0.0.1 h1:LgD6+skofkOx8z6odD9+MZHKjupv3ng1u6KRhaADTnA=
|
||||
github.com/libp2p/go-libp2p-netutil v0.0.1/go.mod h1:GdusFvujWZI9Vt0X5BKqwWWmZFxecf9Gt03cKxm2f/Q=
|
||||
github.com/libp2p/go-libp2p-peer v0.0.1 h1:0qwAOljzYewINrU+Kndoc+1jAL7vzY/oY2Go4DCGfyY=
|
||||
github.com/libp2p/go-libp2p-peer v0.0.1/go.mod h1:nXQvOBbwVqoP+T5Y5nCjeH4sP9IX/J0AMzcDUVruVoo=
|
||||
github.com/libp2p/go-libp2p-peerstore v0.0.1 h1:twKovq8YK5trLrd3nB7PD2Zu9JcyAIdm7Bz9yBWjhq8=
|
||||
github.com/libp2p/go-libp2p-peerstore v0.0.1/go.mod h1:RabLyPVJLuNQ+GFyoEkfi8H4Ti6k/HtZJ7YKgtSq+20=
|
||||
github.com/libp2p/go-libp2p-peerstore v0.0.2 h1:Lirt3A1Oq11jszJ4SPNBo8chNv61UWXE538KUEGxTVk=
|
||||
github.com/libp2p/go-libp2p-peerstore v0.0.2/go.mod h1:RabLyPVJLuNQ+GFyoEkfi8H4Ti6k/HtZJ7YKgtSq+20=
|
||||
github.com/libp2p/go-libp2p-protocol v0.0.1 h1:+zkEmZ2yFDi5adpVE3t9dqh/N9TbpFWywowzeEzBbLM=
|
||||
github.com/libp2p/go-libp2p-protocol v0.0.1/go.mod h1:Af9n4PiruirSDjHycM1QuiMi/1VZNHYcK8cLgFJLZ4s=
|
||||
github.com/libp2p/go-libp2p-routing v0.0.1 h1:hPMAWktf9rYi3ME4MG48qE7dq1ofJxiQbfdvpNntjhc=
|
||||
github.com/libp2p/go-libp2p-routing v0.0.1/go.mod h1:N51q3yTr4Zdr7V8Jt2JIktVU+3xBBylx1MZeVA6t1Ys=
|
||||
github.com/libp2p/go-libp2p-secio v0.0.1 h1:CqE/RdsizOwItdgLe632iyft/w0tshDLmZGAiKDcUAI=
|
||||
github.com/libp2p/go-libp2p-secio v0.0.1/go.mod h1:IdG6iQybdcYmbTzxp4J5dwtUEDTOvZrT0opIDVNPrJs=
|
||||
github.com/libp2p/go-libp2p-secio v0.0.2 h1:P8Sv1aAQ9bdLb58T4T7kUC1kXsPovdUFhUSmVy4z3oM=
|
||||
github.com/libp2p/go-libp2p-secio v0.0.2/go.mod h1:hS7HQ00MgLhRO/Wyu1bTX6ctJKhVpm+j2/S2A5UqYb0=
|
||||
github.com/libp2p/go-libp2p-swarm v0.0.1 h1:Vne+hjaDwXqzgNwQ2vb2YKbnbOTyXjtS47stT66Apc4=
|
||||
github.com/libp2p/go-libp2p-swarm v0.0.1/go.mod h1:mh+KZxkbd3lQnveQ3j2q60BM1Cw2mX36XXQqwfPOShs=
|
||||
github.com/libp2p/go-libp2p-swarm v0.0.2 h1:cpHHXTeU2IgUu8LPemF7vaLPGtVC6VxMoll2EwqlC+E=
|
||||
github.com/libp2p/go-libp2p-swarm v0.0.2/go.mod h1:n0cAAcKyndIrJWctQwjqXlAdIPBZzfdpBjx1SSvz30g=
|
||||
github.com/libp2p/go-libp2p-transport v0.0.1/go.mod h1:UzbUs9X+PHOSw7S3ZmeOxfnwaQY5vGDzZmKPod3N3tk=
|
||||
github.com/libp2p/go-libp2p-transport v0.0.4 h1:/CPHQMN75/IQwkhBxxIo6p6PtL3rwFZtlzBROT3e8mw=
|
||||
github.com/libp2p/go-libp2p-transport v0.0.4/go.mod h1:StoY3sx6IqsP6XKoabsPnHCwqKXWUMWU7Rfcsubee/A=
|
||||
github.com/libp2p/go-libp2p-transport-upgrader v0.0.1 h1:rNtXkY6dty46mxYOHHAZQchI7gQdJStF683FhVnei/k=
|
||||
github.com/libp2p/go-libp2p-transport-upgrader v0.0.1/go.mod h1:NJpUAgQab/8K6K0m+JmZCe5RUXG10UMEx4kWe9Ipj5c=
|
||||
github.com/libp2p/go-maddr-filter v0.0.1 h1:apvYTg0aIxxQyBX+XHKOR+0+lYhGs1Yv+JmTH9nyl5I=
|
||||
github.com/libp2p/go-maddr-filter v0.0.1/go.mod h1:6eT12kSQMA9x2pvFQa+xesMKUBlj9VImZbj3B9FBH/Q=
|
||||
github.com/libp2p/go-mplex v0.0.1 h1:dn2XGSrUxLtz3/8u85bGrwhUEKPX8MOF3lpmcWBZCWc=
|
||||
github.com/libp2p/go-mplex v0.0.1/go.mod h1:pK5yMLmOoBR1pNCqDlA2GQrdAVTMkqFalaTWe7l4Yd0=
|
||||
github.com/libp2p/go-msgio v0.0.1 h1:znj97n5FtXGCLDwe9x8jpHmY770SW4WStBGcCDh6GJw=
|
||||
github.com/libp2p/go-msgio v0.0.1/go.mod h1:63lBBgOTDKQL6EWazRMCwXsEeEeK9O2Cd+0+6OOuipQ=
|
||||
github.com/libp2p/go-msgio v0.0.2 h1:ivPvEKHxmVkTClHzg6RXTYHqaJQ0V9cDbq+6lKb3UV0=
|
||||
github.com/libp2p/go-msgio v0.0.2/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-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.1 h1:UIRneNxLDmEGNjGHpIiWzSWkZ5bhxMCP9x3Vh7BSc7E=
|
||||
github.com/libp2p/go-reuseport-transport v0.0.1/go.mod h1:YkbSDrvjUVDL6b8XqriyA20obEtsW9BLkuOUyQAOCbs=
|
||||
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 h1:Ce6e2Pyu+b5MC1k3eeFtAax0pW4gc6MosYSLV05UeLw=
|
||||
github.com/libp2p/go-stream-muxer v0.0.1/go.mod h1:bAo8x7YkSpadMTbtTaxGVHWUQsR/l5MEaHbKaliuT14=
|
||||
github.com/libp2p/go-tcp-transport v0.0.1 h1:WyvJVw2lYAnr6CU+GZZ4oCt06fvORlmvBlFX2+ZpZDM=
|
||||
github.com/libp2p/go-tcp-transport v0.0.1/go.mod h1:mnjg0o0O5TmXUaUIanYPUqkW4+u6mK0en8rlpA6BBTs=
|
||||
github.com/libp2p/go-tcp-transport v0.0.2 h1:EzOSRaHpyrGpJ5qe+9SaxJM1mrWlkSLFfNTufUkq0lg=
|
||||
github.com/libp2p/go-tcp-transport v0.0.2/go.mod h1:VjZFHasNJ0QiJQNNwiFDy25qyGWTXQWs8GM5DR4/L1k=
|
||||
github.com/libp2p/go-testutil v0.0.1 h1:Xg+O0G2HIMfHqBOBDcMS1iSZJ3GEcId4qOxCQvsGZHk=
|
||||
github.com/libp2p/go-testutil v0.0.1/go.mod h1:iAcJc/DKJQanJ5ws2V+u5ywdL2n12X1WbbEG+Jjy69I=
|
||||
github.com/libp2p/go-ws-transport v0.0.2 h1:PtK1AoM16nm96FwPBQoq+4T4t9LdDwOhkB+mdXuGSlg=
|
||||
github.com/libp2p/go-ws-transport v0.0.2/go.mod h1:p3bKjDWHEgtuKKj+2OdPYs5dAPIjtpQGHF2tJfGz7Ww=
|
||||
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.5 h1:tHXDdz1cpzGaovsTB+TVB8q90WEokoVmfMqoVcrLUgw=
|
||||
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/miekg/dns v1.1.4 h1:rCMZsU2ScVSYcAsOXgmC6+AKOK+6pmQTOcw03nfwYV0=
|
||||
github.com/miekg/dns v1.1.4/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 h1:5W7KhL8HVF3XCFOweFD3BNESdnO8ewyYTFT2R+/b8FQ=
|
||||
github.com/minio/sha256-simd v0.0.0-20190131020904-2d45a736cd16/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U=
|
||||
github.com/mr-tron/base58 v1.1.0 h1:Y51FGVJ91WBqCEabAi5OPUz38eAx8DakuAm5svLcsfQ=
|
||||
github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8=
|
||||
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 h1:/QUV3VBMDI6pi6xfiw7lr6xhDWWvQKn9udPn68kLSdY=
|
||||
github.com/multiformats/go-multiaddr v0.0.1/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-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 h1:HHwN1K12I+XllBCrqKnhX949Orn4oawPkegHMu2vDqQ=
|
||||
github.com/multiformats/go-multihash v0.0.1/go.mod h1:w/5tugSrLEbWqlcgJabL3oHFKTwfvkofsjW2Qa1ct4U=
|
||||
github.com/multiformats/go-multistream v0.0.1 h1:JV4VfSdY9n7ECTtY59/TlSyFCzRILvYx4T4Ws8ZgihU=
|
||||
github.com/multiformats/go-multistream v0.0.1/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 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs=
|
||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU=
|
||||
github.com/onsi/gomega v1.4.3/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/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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||
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/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/go-smux-multiplex v3.0.16+incompatible h1:iqksILj8STw03EJQe7Laj4ubnw+ojOyik18cd5vPL1o=
|
||||
github.com/whyrusleeping/go-smux-multiplex v3.0.16+incompatible/go.mod h1:34LEDbeKFZInPUrAG+bjuJmUXONGdEFW7XL0SpTY1y4=
|
||||
github.com/whyrusleeping/go-smux-multistream v2.0.2+incompatible h1:BdYHctE9HJZLquG9tpTdwWcbG4FaX6tVKPGjCGgiVxo=
|
||||
github.com/whyrusleeping/go-smux-multistream v2.0.2+incompatible/go.mod h1:dRWHHvc4HDQSHh9gbKEBbUZ+f2Q8iZTPG3UOGYODxSQ=
|
||||
github.com/whyrusleeping/go-smux-yamux v2.0.8+incompatible h1:IGm/UP/JpEFS6D787sZnZg7RA6fZIR9c/Ms9DeAVNuk=
|
||||
github.com/whyrusleeping/go-smux-yamux v2.0.8+incompatible/go.mod h1:6qHUzBXUbB9MXmw3AUdB52L8sEb/hScCqOdW2kj/wuI=
|
||||
github.com/whyrusleeping/go-smux-yamux v2.0.9+incompatible h1:nVkExQ7pYlN9e45LcqTCOiDD0904fjtm0flnHZGbXkw=
|
||||
github.com/whyrusleeping/go-smux-yamux v2.0.9+incompatible/go.mod h1:6qHUzBXUbB9MXmw3AUdB52L8sEb/hScCqOdW2kj/wuI=
|
||||
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 h1:nMCC9Pwz1pxfC1Y6mYncdk+kq8d5aLx0Q+/gyZGE44M=
|
||||
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/whyrusleeping/yamux v1.1.5 h1:4CK3aUUJQu0qpKZv5gEWJjNOQtdbdDhVVS6PJ+HimdE=
|
||||
github.com/whyrusleeping/yamux v1.1.5/go.mod h1:E8LnQQ8HKx5KD29HZFUwM1PxCOdPRzGwur1mcYhXcD8=
|
||||
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/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-20190228161510-8dd112bcdc25 h1:jsG6UpNLt9iAsb0S2AGW28DveNzzgmbXR+ENoPjUeIU=
|
||||
golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
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-20190227160552-c95aed5357e7 h1:C2F/nMkR/9sfUTpvR3QrjBuTdvMUC/cFajkphs1YLQo=
|
||||
golang.org/x/net v0.0.0-20190227160552-c95aed5357e7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/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 h1:ZytStCyV048ZqDsWHiYDdoI2Vd4msMcrDECFxS+tL9c=
|
||||
golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
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=
|
||||
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
package libp2p
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
config "github.com/libp2p/go-libp2p/config"
|
||||
|
||||
host "github.com/libp2p/go-libp2p-host"
|
||||
)
|
||||
|
||||
// Config describes a set of settings for a libp2p node
|
||||
type Config = config.Config
|
||||
|
||||
// Option is a libp2p config option that can be given to the libp2p constructor
|
||||
// (`libp2p.New`).
|
||||
type Option = config.Option
|
||||
|
||||
// ChainOptions chains multiple options into a single option.
|
||||
func ChainOptions(opts ...Option) Option {
|
||||
return func(cfg *Config) error {
|
||||
for _, opt := range opts {
|
||||
if opt == nil {
|
||||
continue
|
||||
}
|
||||
if err := opt(cfg); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// New constructs a new libp2p node with the given options, falling back on
|
||||
// reasonable defaults. The defaults are:
|
||||
//
|
||||
// - If no transport and listen addresses are provided, the node listens to
|
||||
// the multiaddresses "/ip4/0.0.0.0/tcp/0" and "/ip6/::/tcp/0";
|
||||
//
|
||||
// - If no transport options are provided, the node uses TCP and websocket
|
||||
// transport protocols;
|
||||
//
|
||||
// - If no multiplexer configuration is provided, the node is configured by
|
||||
// default to use the "yamux/1.0.0" and "mplux/6.7.0" stream connection
|
||||
// multiplexers;
|
||||
//
|
||||
// - If no security transport is provided, the host uses the go-libp2p's secio
|
||||
// encrypted transport to encrypt all traffic;
|
||||
//
|
||||
// - If no peer identity is provided, it generates a random RSA 2048 key-par
|
||||
// and derives a new identity from it;
|
||||
//
|
||||
// - If no peerstore is provided, the host is initialized with an empty
|
||||
// peerstore.
|
||||
//
|
||||
// Canceling the passed context will stop the returned libp2p node.
|
||||
func New(ctx context.Context, opts ...Option) (host.Host, error) {
|
||||
return NewWithoutDefaults(ctx, append(opts, FallbackDefaults)...)
|
||||
}
|
||||
|
||||
// NewWithoutDefaults constructs a new libp2p node with the given options but
|
||||
// *without* falling back on reasonable defaults.
|
||||
//
|
||||
// Warning: This function should not be considered a stable interface. We may
|
||||
// choose to add required services at any time and, by using this function, you
|
||||
// opt-out of any defaults we may provide.
|
||||
func NewWithoutDefaults(ctx context.Context, opts ...Option) (host.Host, error) {
|
||||
var cfg Config
|
||||
if err := cfg.Apply(opts...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return cfg.NewNode(ctx)
|
||||
}
|
||||
+320
@@ -0,0 +1,320 @@
|
||||
package libp2p
|
||||
|
||||
// This file contains all libp2p configuration options (except the defaults,
|
||||
// those are in defaults.go)
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
config "github.com/libp2p/go-libp2p/config"
|
||||
bhost "github.com/libp2p/go-libp2p/p2p/host/basic"
|
||||
|
||||
circuit "github.com/libp2p/go-libp2p-circuit"
|
||||
crypto "github.com/libp2p/go-libp2p-crypto"
|
||||
ifconnmgr "github.com/libp2p/go-libp2p-interface-connmgr"
|
||||
pnet "github.com/libp2p/go-libp2p-interface-pnet"
|
||||
metrics "github.com/libp2p/go-libp2p-metrics"
|
||||
pstore "github.com/libp2p/go-libp2p-peerstore"
|
||||
filter "github.com/libp2p/go-maddr-filter"
|
||||
ma "github.com/multiformats/go-multiaddr"
|
||||
)
|
||||
|
||||
// ListenAddrStrings configures libp2p to listen on the given (unparsed)
|
||||
// addresses.
|
||||
func ListenAddrStrings(s ...string) Option {
|
||||
return func(cfg *Config) error {
|
||||
for _, addrstr := range s {
|
||||
a, err := ma.NewMultiaddr(addrstr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cfg.ListenAddrs = append(cfg.ListenAddrs, a)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// ListenAddrs configures libp2p to listen on the given addresses.
|
||||
func ListenAddrs(addrs ...ma.Multiaddr) Option {
|
||||
return func(cfg *Config) error {
|
||||
cfg.ListenAddrs = append(cfg.ListenAddrs, addrs...)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Security configures libp2p to use the given security transport (or transport
|
||||
// constructor).
|
||||
//
|
||||
// Name is the protocol name.
|
||||
//
|
||||
// The transport can be a constructed security.Transport or a function taking
|
||||
// any subset of this libp2p node's:
|
||||
// * Public key
|
||||
// * Private key
|
||||
// * Peer ID
|
||||
// * Host
|
||||
// * Network
|
||||
// * Peerstore
|
||||
func Security(name string, tpt interface{}) Option {
|
||||
stpt, err := config.SecurityConstructor(tpt)
|
||||
err = traceError(err, 1)
|
||||
return func(cfg *Config) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if cfg.Insecure {
|
||||
return fmt.Errorf("cannot use security transports with an insecure libp2p configuration")
|
||||
}
|
||||
cfg.SecurityTransports = append(cfg.SecurityTransports, config.MsSecC{SecC: stpt, ID: name})
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// NoSecurity is an option that completely disables all transport security.
|
||||
// It's incompatible with all other transport security protocols.
|
||||
var NoSecurity Option = func(cfg *Config) error {
|
||||
if len(cfg.SecurityTransports) > 0 {
|
||||
return fmt.Errorf("cannot use security transports with an insecure libp2p configuration")
|
||||
}
|
||||
cfg.Insecure = true
|
||||
return nil
|
||||
}
|
||||
|
||||
// Muxer configures libp2p to use the given stream multiplexer (or stream
|
||||
// multiplexer constructor).
|
||||
//
|
||||
// Name is the protocol name.
|
||||
//
|
||||
// The transport can be a constructed mux.Transport or a function taking any
|
||||
// subset of this libp2p node's:
|
||||
// * Peer ID
|
||||
// * Host
|
||||
// * Network
|
||||
// * Peerstore
|
||||
func Muxer(name string, tpt interface{}) Option {
|
||||
mtpt, err := config.MuxerConstructor(tpt)
|
||||
err = traceError(err, 1)
|
||||
return func(cfg *Config) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cfg.Muxers = append(cfg.Muxers, config.MsMuxC{MuxC: mtpt, ID: name})
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Transport configures libp2p to use the given transport (or transport
|
||||
// constructor).
|
||||
//
|
||||
// The transport can be a constructed transport.Transport or a function taking
|
||||
// any subset of this libp2p node's:
|
||||
// * Transport Upgrader (*tptu.Upgrader)
|
||||
// * Host
|
||||
// * Stream muxer (muxer.Transport)
|
||||
// * Security transport (security.Transport)
|
||||
// * Private network protector (pnet.Protector)
|
||||
// * Peer ID
|
||||
// * Private Key
|
||||
// * Public Key
|
||||
// * Address filter (filter.Filter)
|
||||
// * Peerstore
|
||||
func Transport(tpt interface{}) Option {
|
||||
tptc, err := config.TransportConstructor(tpt)
|
||||
err = traceError(err, 1)
|
||||
return func(cfg *Config) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cfg.Transports = append(cfg.Transports, tptc)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Peerstore configures libp2p to use the given peerstore.
|
||||
func Peerstore(ps pstore.Peerstore) Option {
|
||||
return func(cfg *Config) error {
|
||||
if cfg.Peerstore != nil {
|
||||
return fmt.Errorf("cannot specify multiple peerstore options")
|
||||
}
|
||||
|
||||
cfg.Peerstore = ps
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// PrivateNetwork configures libp2p to use the given private network protector.
|
||||
func PrivateNetwork(prot pnet.Protector) Option {
|
||||
return func(cfg *Config) error {
|
||||
if cfg.Protector != nil {
|
||||
return fmt.Errorf("cannot specify multiple private network options")
|
||||
}
|
||||
|
||||
cfg.Protector = prot
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// BandwidthReporter configures libp2p to use the given bandwidth reporter.
|
||||
func BandwidthReporter(rep metrics.Reporter) Option {
|
||||
return func(cfg *Config) error {
|
||||
if cfg.Reporter != nil {
|
||||
return fmt.Errorf("cannot specify multiple bandwidth reporter options")
|
||||
}
|
||||
|
||||
cfg.Reporter = rep
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Identity configures libp2p to use the given private key to identify itself.
|
||||
func Identity(sk crypto.PrivKey) Option {
|
||||
return func(cfg *Config) error {
|
||||
if cfg.PeerKey != nil {
|
||||
return fmt.Errorf("cannot specify multiple identities")
|
||||
}
|
||||
|
||||
cfg.PeerKey = sk
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// ConnectionManager configures libp2p to use the given connection manager.
|
||||
func ConnectionManager(connman ifconnmgr.ConnManager) Option {
|
||||
return func(cfg *Config) error {
|
||||
if cfg.ConnManager != nil {
|
||||
return fmt.Errorf("cannot specify multiple connection managers")
|
||||
}
|
||||
cfg.ConnManager = connman
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// AddrsFactory configures libp2p to use the given address factory.
|
||||
func AddrsFactory(factory config.AddrsFactory) Option {
|
||||
return func(cfg *Config) error {
|
||||
if cfg.AddrsFactory != nil {
|
||||
return fmt.Errorf("cannot specify multiple address factories")
|
||||
}
|
||||
cfg.AddrsFactory = factory
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// EnableRelay configures libp2p to enable the relay transport with
|
||||
// configuration options. By default, this option only configures libp2p to
|
||||
// accept inbound connections from relays and make outbound connections
|
||||
// _through_ relays when requested by the remote peer. (default: enabled)
|
||||
//
|
||||
// To _act_ as a relay, pass the circuit.OptHop option.
|
||||
func EnableRelay(options ...circuit.RelayOpt) Option {
|
||||
return func(cfg *Config) error {
|
||||
cfg.RelayCustom = true
|
||||
cfg.Relay = true
|
||||
cfg.RelayOpts = options
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// DisableRelay configures libp2p to disable the relay transport.
|
||||
func DisableRelay() Option {
|
||||
return func(cfg *Config) error {
|
||||
cfg.RelayCustom = true
|
||||
cfg.Relay = false
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// EnableAutoRelay configures libp2p to enable the AutoRelay subsystem. It is an
|
||||
// error to enable AutoRelay without enabling relay (enabled by default) and
|
||||
// routing (not enabled by default).
|
||||
//
|
||||
// This subsystem performs two functions:
|
||||
//
|
||||
// 1. When this libp2p node is configured to act as a relay "hop"
|
||||
// (circuit.OptHop is passed to EnableRelay), this node will advertise itself
|
||||
// as a public relay using the provided routing system.
|
||||
// 2. When this libp2p node is _not_ configured as a relay "hop", it will
|
||||
// automatically detect if it is unreachable (e.g., behind a NAT). If so, it will
|
||||
// find, configure, and announce a set of public relays.
|
||||
func EnableAutoRelay() Option {
|
||||
return func(cfg *Config) error {
|
||||
cfg.EnableAutoRelay = true
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// FilterAddresses configures libp2p to never dial nor accept connections from
|
||||
// the given addresses.
|
||||
func FilterAddresses(addrs ...*net.IPNet) Option {
|
||||
return func(cfg *Config) error {
|
||||
if cfg.Filters == nil {
|
||||
cfg.Filters = filter.NewFilters()
|
||||
}
|
||||
for _, addr := range addrs {
|
||||
cfg.Filters.AddDialFilter(addr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// NATPortMap configures libp2p to use the default NATManager. The default
|
||||
// NATManager will attempt to open a port in your network's firewall using UPnP.
|
||||
func NATPortMap() Option {
|
||||
return NATManager(bhost.NewNATManager)
|
||||
}
|
||||
|
||||
// NATManager will configure libp2p to use the requested NATManager. This
|
||||
// function should be passed a NATManager *constructor* that takes a libp2p Network.
|
||||
func NATManager(nm config.NATManagerC) Option {
|
||||
return func(cfg *Config) error {
|
||||
if cfg.NATManager != nil {
|
||||
return fmt.Errorf("cannot specify multiple NATManagers")
|
||||
}
|
||||
cfg.NATManager = nm
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Ping will configure libp2p to support the ping service; enable by default.
|
||||
func Ping(enable bool) Option {
|
||||
return func(cfg *Config) error {
|
||||
cfg.DisablePing = !enable
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Routing will configure libp2p to use routing.
|
||||
func Routing(rt config.RoutingC) Option {
|
||||
return func(cfg *Config) error {
|
||||
if cfg.Routing != nil {
|
||||
return fmt.Errorf("cannot specify multiple routing options")
|
||||
}
|
||||
cfg.Routing = rt
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// NoListenAddrs will configure libp2p to not listen by default.
|
||||
//
|
||||
// This will both clear any configured listen addrs and prevent libp2p from
|
||||
// applying the default listen address option. It also disables relay, unless the
|
||||
// user explicitly specifies with an option, as the transport creates an implicit
|
||||
// listen address that would make the node dialable through any relay it was connected to.
|
||||
var NoListenAddrs = func(cfg *Config) error {
|
||||
cfg.ListenAddrs = []ma.Multiaddr{}
|
||||
if !cfg.RelayCustom {
|
||||
cfg.RelayCustom = true
|
||||
cfg.Relay = false
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// NoTransports will configure libp2p to not enable any transports.
|
||||
//
|
||||
// This will both clear any configured transports (specified in prior libp2p
|
||||
// options) and prevent libp2p from applying the default transports.
|
||||
var NoTransports = func(cfg *Config) error {
|
||||
cfg.Transports = []config.TptC{}
|
||||
return nil
|
||||
}
|
||||
+207
@@ -0,0 +1,207 @@
|
||||
package discovery
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
logging "github.com/ipfs/go-log"
|
||||
"github.com/libp2p/go-libp2p-host"
|
||||
"github.com/libp2p/go-libp2p-peer"
|
||||
pstore "github.com/libp2p/go-libp2p-peerstore"
|
||||
ma "github.com/multiformats/go-multiaddr"
|
||||
manet "github.com/multiformats/go-multiaddr-net"
|
||||
"github.com/whyrusleeping/mdns"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// don't let mdns use logging...
|
||||
mdns.DisableLogging = true
|
||||
}
|
||||
|
||||
var log = logging.Logger("mdns")
|
||||
|
||||
const ServiceTag = "_ipfs-discovery._udp"
|
||||
|
||||
type Service interface {
|
||||
io.Closer
|
||||
RegisterNotifee(Notifee)
|
||||
UnregisterNotifee(Notifee)
|
||||
}
|
||||
|
||||
type Notifee interface {
|
||||
HandlePeerFound(pstore.PeerInfo)
|
||||
}
|
||||
|
||||
type mdnsService struct {
|
||||
server *mdns.Server
|
||||
service *mdns.MDNSService
|
||||
host host.Host
|
||||
tag string
|
||||
|
||||
lk sync.Mutex
|
||||
notifees []Notifee
|
||||
interval time.Duration
|
||||
}
|
||||
|
||||
func getDialableListenAddrs(ph host.Host) ([]*net.TCPAddr, error) {
|
||||
var out []*net.TCPAddr
|
||||
for _, addr := range ph.Addrs() {
|
||||
na, err := manet.ToNetAddr(addr)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
tcp, ok := na.(*net.TCPAddr)
|
||||
if ok {
|
||||
out = append(out, tcp)
|
||||
}
|
||||
}
|
||||
if len(out) == 0 {
|
||||
return nil, errors.New("failed to find good external addr from peerhost")
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func NewMdnsService(ctx context.Context, peerhost host.Host, interval time.Duration, serviceTag string) (Service, error) {
|
||||
|
||||
var ipaddrs []net.IP
|
||||
port := 4001
|
||||
|
||||
addrs, err := getDialableListenAddrs(peerhost)
|
||||
if err != nil {
|
||||
log.Warning(err)
|
||||
} else {
|
||||
port = addrs[0].Port
|
||||
for _, a := range addrs {
|
||||
ipaddrs = append(ipaddrs, a.IP)
|
||||
}
|
||||
}
|
||||
|
||||
myid := peerhost.ID().Pretty()
|
||||
|
||||
info := []string{myid}
|
||||
if serviceTag == "" {
|
||||
serviceTag = ServiceTag
|
||||
}
|
||||
service, err := mdns.NewMDNSService(myid, serviceTag, "", "", port, ipaddrs, info)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Create the mDNS server, defer shutdown
|
||||
server, err := mdns.NewServer(&mdns.Config{Zone: service})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s := &mdnsService{
|
||||
server: server,
|
||||
service: service,
|
||||
host: peerhost,
|
||||
interval: interval,
|
||||
tag: serviceTag,
|
||||
}
|
||||
|
||||
go s.pollForEntries(ctx)
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func (m *mdnsService) Close() error {
|
||||
return m.server.Shutdown()
|
||||
}
|
||||
|
||||
func (m *mdnsService) pollForEntries(ctx context.Context) {
|
||||
|
||||
ticker := time.NewTicker(m.interval)
|
||||
for {
|
||||
//execute mdns query right away at method call and then with every tick
|
||||
entriesCh := make(chan *mdns.ServiceEntry, 16)
|
||||
go func() {
|
||||
for entry := range entriesCh {
|
||||
m.handleEntry(entry)
|
||||
}
|
||||
}()
|
||||
|
||||
log.Debug("starting mdns query")
|
||||
qp := &mdns.QueryParam{
|
||||
Domain: "local",
|
||||
Entries: entriesCh,
|
||||
Service: m.tag,
|
||||
Timeout: time.Second * 5,
|
||||
}
|
||||
|
||||
err := mdns.Query(qp)
|
||||
if err != nil {
|
||||
log.Error("mdns lookup error: ", err)
|
||||
}
|
||||
close(entriesCh)
|
||||
log.Debug("mdns query complete")
|
||||
|
||||
select {
|
||||
case <-ticker.C:
|
||||
continue
|
||||
case <-ctx.Done():
|
||||
log.Debug("mdns service halting")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *mdnsService) handleEntry(e *mdns.ServiceEntry) {
|
||||
log.Debugf("Handling MDNS entry: %s:%d %s", e.AddrV4, e.Port, e.Info)
|
||||
mpeer, err := peer.IDB58Decode(e.Info)
|
||||
if err != nil {
|
||||
log.Warning("Error parsing peer ID from mdns entry: ", err)
|
||||
return
|
||||
}
|
||||
|
||||
if mpeer == m.host.ID() {
|
||||
log.Debug("got our own mdns entry, skipping")
|
||||
return
|
||||
}
|
||||
|
||||
maddr, err := manet.FromNetAddr(&net.TCPAddr{
|
||||
IP: e.AddrV4,
|
||||
Port: e.Port,
|
||||
})
|
||||
if err != nil {
|
||||
log.Warning("Error parsing multiaddr from mdns entry: ", err)
|
||||
return
|
||||
}
|
||||
|
||||
pi := pstore.PeerInfo{
|
||||
ID: mpeer,
|
||||
Addrs: []ma.Multiaddr{maddr},
|
||||
}
|
||||
|
||||
m.lk.Lock()
|
||||
for _, n := range m.notifees {
|
||||
go n.HandlePeerFound(pi)
|
||||
}
|
||||
m.lk.Unlock()
|
||||
}
|
||||
|
||||
func (m *mdnsService) RegisterNotifee(n Notifee) {
|
||||
m.lk.Lock()
|
||||
m.notifees = append(m.notifees, n)
|
||||
m.lk.Unlock()
|
||||
}
|
||||
|
||||
func (m *mdnsService) UnregisterNotifee(n Notifee) {
|
||||
m.lk.Lock()
|
||||
found := -1
|
||||
for i, notif := range m.notifees {
|
||||
if notif == n {
|
||||
found = i
|
||||
break
|
||||
}
|
||||
}
|
||||
if found != -1 {
|
||||
m.notifees = append(m.notifees[:found], m.notifees[found+1:]...)
|
||||
}
|
||||
m.lk.Unlock()
|
||||
}
|
||||
+736
@@ -0,0 +1,736 @@
|
||||
package basichost
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
logging "github.com/ipfs/go-log"
|
||||
goprocess "github.com/jbenet/goprocess"
|
||||
goprocessctx "github.com/jbenet/goprocess/context"
|
||||
ifconnmgr "github.com/libp2p/go-libp2p-interface-connmgr"
|
||||
inat "github.com/libp2p/go-libp2p-nat"
|
||||
inet "github.com/libp2p/go-libp2p-net"
|
||||
peer "github.com/libp2p/go-libp2p-peer"
|
||||
pstore "github.com/libp2p/go-libp2p-peerstore"
|
||||
protocol "github.com/libp2p/go-libp2p-protocol"
|
||||
identify "github.com/libp2p/go-libp2p/p2p/protocol/identify"
|
||||
ping "github.com/libp2p/go-libp2p/p2p/protocol/ping"
|
||||
ma "github.com/multiformats/go-multiaddr"
|
||||
madns "github.com/multiformats/go-multiaddr-dns"
|
||||
manet "github.com/multiformats/go-multiaddr-net"
|
||||
msmux "github.com/multiformats/go-multistream"
|
||||
)
|
||||
|
||||
var log = logging.Logger("basichost")
|
||||
|
||||
var (
|
||||
// DefaultNegotiationTimeout is the default value for HostOpts.NegotiationTimeout.
|
||||
DefaultNegotiationTimeout = time.Second * 60
|
||||
|
||||
// DefaultAddrsFactory is the default value for HostOpts.AddrsFactory.
|
||||
DefaultAddrsFactory = func(addrs []ma.Multiaddr) []ma.Multiaddr { return addrs }
|
||||
)
|
||||
|
||||
// AddrsFactory functions can be passed to New in order to override
|
||||
// addresses returned by Addrs.
|
||||
type AddrsFactory func([]ma.Multiaddr) []ma.Multiaddr
|
||||
|
||||
// Option is a type used to pass in options to the host.
|
||||
//
|
||||
// Deprecated in favor of HostOpts and NewHost.
|
||||
type Option int
|
||||
|
||||
// NATPortMap makes the host attempt to open port-mapping in NAT devices
|
||||
// for all its listeners. Pass in this option in the constructor to
|
||||
// asynchronously a) find a gateway, b) open port mappings, c) republish
|
||||
// port mappings periodically. The NATed addresses are included in the
|
||||
// Host's Addrs() list.
|
||||
//
|
||||
// This option is deprecated in favor of HostOpts and NewHost.
|
||||
const NATPortMap Option = iota
|
||||
|
||||
// BasicHost is the basic implementation of the host.Host interface. This
|
||||
// particular host implementation:
|
||||
// * uses a protocol muxer to mux per-protocol streams
|
||||
// * uses an identity service to send + receive node information
|
||||
// * uses a nat service to establish NAT port mappings
|
||||
type BasicHost struct {
|
||||
network inet.Network
|
||||
mux *msmux.MultistreamMuxer
|
||||
ids *identify.IDService
|
||||
pings *ping.PingService
|
||||
natmgr NATManager
|
||||
maResolver *madns.Resolver
|
||||
cmgr ifconnmgr.ConnManager
|
||||
|
||||
AddrsFactory AddrsFactory
|
||||
|
||||
negtimeout time.Duration
|
||||
|
||||
proc goprocess.Process
|
||||
|
||||
ctx context.Context
|
||||
cancel func()
|
||||
mx sync.Mutex
|
||||
lastAddrs []ma.Multiaddr
|
||||
}
|
||||
|
||||
// HostOpts holds options that can be passed to NewHost in order to
|
||||
// customize construction of the *BasicHost.
|
||||
type HostOpts struct {
|
||||
|
||||
// MultistreamMuxer is essential for the *BasicHost and will use a sensible default value if omitted.
|
||||
MultistreamMuxer *msmux.MultistreamMuxer
|
||||
|
||||
// NegotiationTimeout determines the read and write timeouts on streams.
|
||||
// If 0 or omitted, it will use DefaultNegotiationTimeout.
|
||||
// If below 0, timeouts on streams will be deactivated.
|
||||
NegotiationTimeout time.Duration
|
||||
|
||||
// IdentifyService holds an implementation of the /ipfs/id/ protocol.
|
||||
// If omitted, a new *identify.IDService will be used.
|
||||
IdentifyService *identify.IDService
|
||||
|
||||
// AddrsFactory holds a function which can be used to override or filter the result of Addrs.
|
||||
// If omitted, there's no override or filtering, and the results of Addrs and AllAddrs are the same.
|
||||
AddrsFactory AddrsFactory
|
||||
|
||||
// MultiaddrResolves holds the go-multiaddr-dns.Resolver used for resolving
|
||||
// /dns4, /dns6, and /dnsaddr addresses before trying to connect to a peer.
|
||||
MultiaddrResolver *madns.Resolver
|
||||
|
||||
// NATManager takes care of setting NAT port mappings, and discovering external addresses.
|
||||
// If omitted, this will simply be disabled.
|
||||
NATManager func(inet.Network) NATManager
|
||||
|
||||
// ConnManager is a libp2p connection manager
|
||||
ConnManager ifconnmgr.ConnManager
|
||||
|
||||
// EnablePing indicates whether to instantiate the ping service
|
||||
EnablePing bool
|
||||
}
|
||||
|
||||
// NewHost constructs a new *BasicHost and activates it by attaching its stream and connection handlers to the given inet.Network.
|
||||
func NewHost(ctx context.Context, net inet.Network, opts *HostOpts) (*BasicHost, error) {
|
||||
h := &BasicHost{
|
||||
network: net,
|
||||
mux: msmux.NewMultistreamMuxer(),
|
||||
negtimeout: DefaultNegotiationTimeout,
|
||||
AddrsFactory: DefaultAddrsFactory,
|
||||
maResolver: madns.DefaultResolver,
|
||||
}
|
||||
|
||||
h.proc = goprocessctx.WithContextAndTeardown(ctx, func() error {
|
||||
if h.natmgr != nil {
|
||||
h.natmgr.Close()
|
||||
}
|
||||
return h.Network().Close()
|
||||
})
|
||||
|
||||
if opts.MultistreamMuxer != nil {
|
||||
h.mux = opts.MultistreamMuxer
|
||||
}
|
||||
|
||||
if opts.IdentifyService != nil {
|
||||
h.ids = opts.IdentifyService
|
||||
} else {
|
||||
// we can't set this as a default above because it depends on the *BasicHost.
|
||||
h.ids = identify.NewIDService(ctx, h)
|
||||
}
|
||||
|
||||
if uint64(opts.NegotiationTimeout) != 0 {
|
||||
h.negtimeout = opts.NegotiationTimeout
|
||||
}
|
||||
|
||||
if opts.AddrsFactory != nil {
|
||||
h.AddrsFactory = opts.AddrsFactory
|
||||
}
|
||||
|
||||
if opts.NATManager != nil {
|
||||
h.natmgr = opts.NATManager(net)
|
||||
}
|
||||
|
||||
if opts.MultiaddrResolver != nil {
|
||||
h.maResolver = opts.MultiaddrResolver
|
||||
}
|
||||
|
||||
if opts.ConnManager == nil {
|
||||
h.cmgr = &ifconnmgr.NullConnMgr{}
|
||||
} else {
|
||||
h.cmgr = opts.ConnManager
|
||||
net.Notify(h.cmgr.Notifee())
|
||||
}
|
||||
|
||||
if opts.EnablePing {
|
||||
h.pings = ping.NewPingService(h)
|
||||
}
|
||||
|
||||
net.SetConnHandler(h.newConnHandler)
|
||||
net.SetStreamHandler(h.newStreamHandler)
|
||||
|
||||
bgctx, cancel := context.WithCancel(ctx)
|
||||
h.ctx = bgctx
|
||||
h.cancel = cancel
|
||||
|
||||
return h, nil
|
||||
}
|
||||
|
||||
// New constructs and sets up a new *BasicHost with given Network and options.
|
||||
// The following options can be passed:
|
||||
// * NATPortMap
|
||||
// * AddrsFactory
|
||||
// * ifconnmgr.ConnManager
|
||||
// * madns.Resolver
|
||||
//
|
||||
// This function is deprecated in favor of NewHost and HostOpts.
|
||||
func New(net inet.Network, opts ...interface{}) *BasicHost {
|
||||
hostopts := &HostOpts{}
|
||||
|
||||
for _, o := range opts {
|
||||
switch o := o.(type) {
|
||||
case Option:
|
||||
switch o {
|
||||
case NATPortMap:
|
||||
hostopts.NATManager = NewNATManager
|
||||
}
|
||||
case AddrsFactory:
|
||||
hostopts.AddrsFactory = AddrsFactory(o)
|
||||
case ifconnmgr.ConnManager:
|
||||
hostopts.ConnManager = o
|
||||
case *madns.Resolver:
|
||||
hostopts.MultiaddrResolver = o
|
||||
}
|
||||
}
|
||||
|
||||
h, err := NewHost(context.Background(), net, hostopts)
|
||||
if err != nil {
|
||||
// this cannot happen with legacy options
|
||||
// plus we want to keep the (deprecated) legacy interface unchanged
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return h
|
||||
}
|
||||
|
||||
// Start starts background tasks in the host
|
||||
func (h *BasicHost) Start() {
|
||||
go h.background()
|
||||
}
|
||||
|
||||
// newConnHandler is the remote-opened conn handler for inet.Network
|
||||
func (h *BasicHost) newConnHandler(c inet.Conn) {
|
||||
// Clear protocols on connecting to new peer to avoid issues caused
|
||||
// by misremembering protocols between reconnects
|
||||
h.Peerstore().SetProtocols(c.RemotePeer())
|
||||
h.ids.IdentifyConn(c)
|
||||
}
|
||||
|
||||
// newStreamHandler is the remote-opened stream handler for inet.Network
|
||||
// TODO: this feels a bit wonky
|
||||
func (h *BasicHost) newStreamHandler(s inet.Stream) {
|
||||
before := time.Now()
|
||||
|
||||
if h.negtimeout > 0 {
|
||||
if err := s.SetDeadline(time.Now().Add(h.negtimeout)); err != nil {
|
||||
log.Error("setting stream deadline: ", err)
|
||||
s.Reset()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
lzc, protoID, handle, err := h.Mux().NegotiateLazy(s)
|
||||
took := time.Now().Sub(before)
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
logf := log.Debugf
|
||||
if took > time.Second*10 {
|
||||
logf = log.Warningf
|
||||
}
|
||||
logf("protocol EOF: %s (took %s)", s.Conn().RemotePeer(), took)
|
||||
} else {
|
||||
log.Debugf("protocol mux failed: %s (took %s)", err, took)
|
||||
}
|
||||
s.Reset()
|
||||
return
|
||||
}
|
||||
|
||||
s = &streamWrapper{
|
||||
Stream: s,
|
||||
rw: lzc,
|
||||
}
|
||||
|
||||
if h.negtimeout > 0 {
|
||||
if err := s.SetDeadline(time.Time{}); err != nil {
|
||||
log.Error("resetting stream deadline: ", err)
|
||||
s.Reset()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
s.SetProtocol(protocol.ID(protoID))
|
||||
log.Debugf("protocol negotiation took %s", took)
|
||||
|
||||
go handle(protoID, s)
|
||||
}
|
||||
|
||||
// PushIdentify pushes an identify update through the identify push protocol
|
||||
// Warning: this interface is unstable and may disappear in the future.
|
||||
func (h *BasicHost) PushIdentify() {
|
||||
push := false
|
||||
|
||||
h.mx.Lock()
|
||||
addrs := h.Addrs()
|
||||
if !sameAddrs(addrs, h.lastAddrs) {
|
||||
push = true
|
||||
h.lastAddrs = addrs
|
||||
}
|
||||
h.mx.Unlock()
|
||||
|
||||
if push {
|
||||
h.ids.Push()
|
||||
}
|
||||
}
|
||||
|
||||
func (h *BasicHost) background() {
|
||||
// periodically schedules an IdentifyPush to update our peers for changes
|
||||
// in our address set (if needed)
|
||||
ticker := time.NewTicker(1 * time.Minute)
|
||||
defer ticker.Stop()
|
||||
|
||||
// initialize lastAddrs
|
||||
h.mx.Lock()
|
||||
if h.lastAddrs == nil {
|
||||
h.lastAddrs = h.Addrs()
|
||||
}
|
||||
h.mx.Unlock()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
h.PushIdentify()
|
||||
|
||||
case <-h.ctx.Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func sameAddrs(a, b []ma.Multiaddr) bool {
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
|
||||
bmap := make(map[string]struct{}, len(b))
|
||||
for _, addr := range b {
|
||||
bmap[string(addr.Bytes())] = struct{}{}
|
||||
}
|
||||
|
||||
for _, addr := range a {
|
||||
_, ok := bmap[string(addr.Bytes())]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// ID returns the (local) peer.ID associated with this Host
|
||||
func (h *BasicHost) ID() peer.ID {
|
||||
return h.Network().LocalPeer()
|
||||
}
|
||||
|
||||
// Peerstore returns the Host's repository of Peer Addresses and Keys.
|
||||
func (h *BasicHost) Peerstore() pstore.Peerstore {
|
||||
return h.Network().Peerstore()
|
||||
}
|
||||
|
||||
// Network returns the Network interface of the Host
|
||||
func (h *BasicHost) Network() inet.Network {
|
||||
return h.network
|
||||
}
|
||||
|
||||
// Mux returns the Mux multiplexing incoming streams to protocol handlers
|
||||
func (h *BasicHost) Mux() *msmux.MultistreamMuxer {
|
||||
return h.mux
|
||||
}
|
||||
|
||||
// IDService returns
|
||||
func (h *BasicHost) IDService() *identify.IDService {
|
||||
return h.ids
|
||||
}
|
||||
|
||||
// SetStreamHandler sets the protocol handler on the Host's Mux.
|
||||
// This is equivalent to:
|
||||
// host.Mux().SetHandler(proto, handler)
|
||||
// (Threadsafe)
|
||||
func (h *BasicHost) SetStreamHandler(pid protocol.ID, handler inet.StreamHandler) {
|
||||
h.Mux().AddHandler(string(pid), func(p string, rwc io.ReadWriteCloser) error {
|
||||
is := rwc.(inet.Stream)
|
||||
is.SetProtocol(protocol.ID(p))
|
||||
handler(is)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// SetStreamHandlerMatch sets the protocol handler on the Host's Mux
|
||||
// using a matching function to do protocol comparisons
|
||||
func (h *BasicHost) SetStreamHandlerMatch(pid protocol.ID, m func(string) bool, handler inet.StreamHandler) {
|
||||
h.Mux().AddHandlerWithFunc(string(pid), m, func(p string, rwc io.ReadWriteCloser) error {
|
||||
is := rwc.(inet.Stream)
|
||||
is.SetProtocol(protocol.ID(p))
|
||||
handler(is)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// RemoveStreamHandler returns ..
|
||||
func (h *BasicHost) RemoveStreamHandler(pid protocol.ID) {
|
||||
h.Mux().RemoveHandler(string(pid))
|
||||
}
|
||||
|
||||
// NewStream opens a new stream to given peer p, and writes a p2p/protocol
|
||||
// header with given protocol.ID. If there is no connection to p, attempts
|
||||
// to create one. If ProtocolID is "", writes no header.
|
||||
// (Threadsafe)
|
||||
func (h *BasicHost) NewStream(ctx context.Context, p peer.ID, pids ...protocol.ID) (inet.Stream, error) {
|
||||
pref, err := h.preferredProtocol(p, pids)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if pref != "" {
|
||||
return h.newStream(ctx, p, pref)
|
||||
}
|
||||
|
||||
var protoStrs []string
|
||||
for _, pid := range pids {
|
||||
protoStrs = append(protoStrs, string(pid))
|
||||
}
|
||||
|
||||
s, err := h.Network().NewStream(ctx, p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
selected, err := msmux.SelectOneOf(protoStrs, s)
|
||||
if err != nil {
|
||||
s.Reset()
|
||||
return nil, err
|
||||
}
|
||||
selpid := protocol.ID(selected)
|
||||
s.SetProtocol(selpid)
|
||||
h.Peerstore().AddProtocols(p, selected)
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func pidsToStrings(pids []protocol.ID) []string {
|
||||
out := make([]string, len(pids))
|
||||
for i, p := range pids {
|
||||
out[i] = string(p)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func (h *BasicHost) preferredProtocol(p peer.ID, pids []protocol.ID) (protocol.ID, error) {
|
||||
pidstrs := pidsToStrings(pids)
|
||||
supported, err := h.Peerstore().SupportsProtocols(p, pidstrs...)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
var out protocol.ID
|
||||
if len(supported) > 0 {
|
||||
out = protocol.ID(supported[0])
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (h *BasicHost) newStream(ctx context.Context, p peer.ID, pid protocol.ID) (inet.Stream, error) {
|
||||
s, err := h.Network().NewStream(ctx, p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s.SetProtocol(pid)
|
||||
|
||||
lzcon := msmux.NewMSSelect(s, string(pid))
|
||||
return &streamWrapper{
|
||||
Stream: s,
|
||||
rw: lzcon,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Connect ensures there is a connection between this host and the peer with
|
||||
// given peer.ID. If there is not an active connection, Connect will issue a
|
||||
// h.Network.Dial, and block until a connection is open, or an error is returned.
|
||||
// Connect will absorb the addresses in pi into its internal peerstore.
|
||||
// It will also resolve any /dns4, /dns6, and /dnsaddr addresses.
|
||||
func (h *BasicHost) Connect(ctx context.Context, pi pstore.PeerInfo) error {
|
||||
// absorb addresses into peerstore
|
||||
h.Peerstore().AddAddrs(pi.ID, pi.Addrs, pstore.TempAddrTTL)
|
||||
|
||||
if h.Network().Connectedness(pi.ID) == inet.Connected {
|
||||
return nil
|
||||
}
|
||||
|
||||
resolved, err := h.resolveAddrs(ctx, h.Peerstore().PeerInfo(pi.ID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
h.Peerstore().AddAddrs(pi.ID, resolved, pstore.TempAddrTTL)
|
||||
|
||||
return h.dialPeer(ctx, pi.ID)
|
||||
}
|
||||
|
||||
func (h *BasicHost) resolveAddrs(ctx context.Context, pi pstore.PeerInfo) ([]ma.Multiaddr, error) {
|
||||
proto := ma.ProtocolWithCode(ma.P_P2P).Name
|
||||
p2paddr, err := ma.NewMultiaddr("/" + proto + "/" + pi.ID.Pretty())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var addrs []ma.Multiaddr
|
||||
for _, addr := range pi.Addrs {
|
||||
addrs = append(addrs, addr)
|
||||
if !madns.Matches(addr) {
|
||||
continue
|
||||
}
|
||||
|
||||
reqaddr := addr.Encapsulate(p2paddr)
|
||||
resaddrs, err := h.maResolver.Resolve(ctx, reqaddr)
|
||||
if err != nil {
|
||||
log.Infof("error resolving %s: %s", reqaddr, err)
|
||||
}
|
||||
for _, res := range resaddrs {
|
||||
pi, err := pstore.InfoFromP2pAddr(res)
|
||||
if err != nil {
|
||||
log.Infof("error parsing %s: %s", res, err)
|
||||
}
|
||||
addrs = append(addrs, pi.Addrs...)
|
||||
}
|
||||
}
|
||||
|
||||
return addrs, nil
|
||||
}
|
||||
|
||||
// dialPeer opens a connection to peer, and makes sure to identify
|
||||
// the connection once it has been opened.
|
||||
func (h *BasicHost) dialPeer(ctx context.Context, p peer.ID) error {
|
||||
log.Debugf("host %s dialing %s", h.ID(), p)
|
||||
c, err := h.Network().DialPeer(ctx, p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Clear protocols on connecting to new peer to avoid issues caused
|
||||
// by misremembering protocols between reconnects
|
||||
h.Peerstore().SetProtocols(p)
|
||||
|
||||
// identify the connection before returning.
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
h.ids.IdentifyConn(c)
|
||||
close(done)
|
||||
}()
|
||||
|
||||
// respect don contexteone
|
||||
select {
|
||||
case <-done:
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
}
|
||||
|
||||
log.Debugf("host %s finished dialing %s", h.ID(), p)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *BasicHost) ConnManager() ifconnmgr.ConnManager {
|
||||
return h.cmgr
|
||||
}
|
||||
|
||||
// Addrs returns listening addresses that are safe to announce to the network.
|
||||
// The output is the same as AllAddrs, but processed by AddrsFactory.
|
||||
func (h *BasicHost) Addrs() []ma.Multiaddr {
|
||||
return h.AddrsFactory(h.AllAddrs())
|
||||
}
|
||||
|
||||
// mergeAddrs merges input address lists, leave only unique addresses
|
||||
func dedupAddrs(addrs []ma.Multiaddr) (uniqueAddrs []ma.Multiaddr) {
|
||||
exists := make(map[string]bool)
|
||||
for _, addr := range addrs {
|
||||
k := string(addr.Bytes())
|
||||
if exists[k] {
|
||||
continue
|
||||
}
|
||||
exists[k] = true
|
||||
uniqueAddrs = append(uniqueAddrs, addr)
|
||||
}
|
||||
return uniqueAddrs
|
||||
}
|
||||
|
||||
// AllAddrs returns all the addresses of BasicHost at this moment in time.
|
||||
// It's ok to not include addresses if they're not available to be used now.
|
||||
func (h *BasicHost) AllAddrs() []ma.Multiaddr {
|
||||
listenAddrs, err := h.Network().InterfaceListenAddresses()
|
||||
if err != nil {
|
||||
log.Debug("error retrieving network interface addrs")
|
||||
}
|
||||
var natMappings []inat.Mapping
|
||||
|
||||
// natmgr is nil if we do not use nat option;
|
||||
// h.natmgr.NAT() is nil if not ready, or no nat is available.
|
||||
if h.natmgr != nil && h.natmgr.NAT() != nil {
|
||||
natMappings = h.natmgr.NAT().Mappings()
|
||||
}
|
||||
|
||||
finalAddrs := listenAddrs
|
||||
if len(natMappings) > 0 {
|
||||
|
||||
// We have successfully mapped ports on our NAT. Use those
|
||||
// instead of observed addresses (mostly).
|
||||
|
||||
// First, generate a mapping table.
|
||||
// protocol -> internal port -> external addr
|
||||
ports := make(map[string]map[int]net.Addr)
|
||||
for _, m := range natMappings {
|
||||
addr, err := m.ExternalAddr()
|
||||
if err != nil {
|
||||
// mapping not ready yet.
|
||||
continue
|
||||
}
|
||||
protoPorts, ok := ports[m.Protocol()]
|
||||
if !ok {
|
||||
protoPorts = make(map[int]net.Addr)
|
||||
ports[m.Protocol()] = protoPorts
|
||||
}
|
||||
protoPorts[m.InternalPort()] = addr
|
||||
}
|
||||
|
||||
// Next, apply this mapping to our addresses.
|
||||
for _, listen := range listenAddrs {
|
||||
found := false
|
||||
transport, rest := ma.SplitFunc(listen, func(c ma.Component) bool {
|
||||
if found {
|
||||
return true
|
||||
}
|
||||
switch c.Protocol().Code {
|
||||
case ma.P_TCP, ma.P_UDP:
|
||||
found = true
|
||||
}
|
||||
return false
|
||||
})
|
||||
if !manet.IsThinWaist(transport) {
|
||||
continue
|
||||
}
|
||||
|
||||
naddr, err := manet.ToNetAddr(transport)
|
||||
if err != nil {
|
||||
log.Error("error parsing net multiaddr %q: %s", transport, err)
|
||||
continue
|
||||
}
|
||||
|
||||
var (
|
||||
ip net.IP
|
||||
iport int
|
||||
protocol string
|
||||
)
|
||||
switch naddr := naddr.(type) {
|
||||
case *net.TCPAddr:
|
||||
ip = naddr.IP
|
||||
iport = naddr.Port
|
||||
protocol = "tcp"
|
||||
case *net.UDPAddr:
|
||||
ip = naddr.IP
|
||||
iport = naddr.Port
|
||||
protocol = "udp"
|
||||
default:
|
||||
continue
|
||||
}
|
||||
|
||||
if !ip.IsGlobalUnicast() {
|
||||
// We only map global unicast ports.
|
||||
continue
|
||||
}
|
||||
|
||||
mappedAddr, ok := ports[protocol][iport]
|
||||
if !ok {
|
||||
// Not mapped.
|
||||
continue
|
||||
}
|
||||
|
||||
mappedMaddr, err := manet.FromNetAddr(mappedAddr)
|
||||
if err != nil {
|
||||
log.Errorf("mapped addr can't be turned into a multiaddr %q: %s", mappedAddr, err)
|
||||
continue
|
||||
}
|
||||
|
||||
// Did the router give us a routable public addr?
|
||||
if manet.IsPublicAddr(mappedMaddr) {
|
||||
// Yes, use it.
|
||||
extMaddr := mappedMaddr
|
||||
if rest != nil {
|
||||
extMaddr = ma.Join(extMaddr, rest)
|
||||
}
|
||||
|
||||
// Add in the mapped addr.
|
||||
finalAddrs = append(finalAddrs, extMaddr)
|
||||
continue
|
||||
}
|
||||
|
||||
// No. Ok, let's try our observed addresses.
|
||||
|
||||
// Now, check if we have any observed addresses that
|
||||
// differ from the one reported by the router. Routers
|
||||
// don't always give the most accurate information.
|
||||
observed := h.ids.ObservedAddrsFor(listen)
|
||||
|
||||
if len(observed) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
// Drop the IP from the external maddr
|
||||
_, extMaddrNoIP := ma.SplitFirst(mappedMaddr)
|
||||
|
||||
for _, obsMaddr := range observed {
|
||||
// Extract a public observed addr.
|
||||
ip, _ := ma.SplitFirst(obsMaddr)
|
||||
if ip == nil || !manet.IsPublicAddr(ip) {
|
||||
continue
|
||||
}
|
||||
|
||||
finalAddrs = append(finalAddrs, ma.Join(ip, extMaddrNoIP))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
var observedAddrs []ma.Multiaddr
|
||||
if h.ids != nil {
|
||||
observedAddrs = h.ids.OwnObservedAddrs()
|
||||
}
|
||||
finalAddrs = append(finalAddrs, observedAddrs...)
|
||||
}
|
||||
return dedupAddrs(finalAddrs)
|
||||
}
|
||||
|
||||
// Close shuts down the Host's services (network, etc).
|
||||
func (h *BasicHost) Close() error {
|
||||
h.cancel()
|
||||
return h.proc.Close()
|
||||
}
|
||||
|
||||
type streamWrapper struct {
|
||||
inet.Stream
|
||||
rw io.ReadWriter
|
||||
}
|
||||
|
||||
func (s *streamWrapper) Read(b []byte) (int, error) {
|
||||
return s.rw.Read(b)
|
||||
}
|
||||
|
||||
func (s *streamWrapper) Write(b []byte) (int, error) {
|
||||
return s.rw.Write(b)
|
||||
}
|
||||
+243
@@ -0,0 +1,243 @@
|
||||
package basichost
|
||||
|
||||
import (
|
||||
"net"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
goprocess "github.com/jbenet/goprocess"
|
||||
goprocessctx "github.com/jbenet/goprocess/context"
|
||||
inat "github.com/libp2p/go-libp2p-nat"
|
||||
inet "github.com/libp2p/go-libp2p-net"
|
||||
ma "github.com/multiformats/go-multiaddr"
|
||||
)
|
||||
|
||||
// A simple interface to manage NAT devices.
|
||||
type NATManager interface {
|
||||
|
||||
// Get the NAT device managed by the NAT manager.
|
||||
NAT() *inat.NAT
|
||||
|
||||
// Receive a notification when the NAT device is ready for use.
|
||||
Ready() <-chan struct{}
|
||||
|
||||
// Close all resources associated with a NAT manager.
|
||||
Close() error
|
||||
}
|
||||
|
||||
// Create a NAT manager.
|
||||
func NewNATManager(net inet.Network) NATManager {
|
||||
return newNatManager(net)
|
||||
}
|
||||
|
||||
// natManager takes care of adding + removing port mappings to the nat.
|
||||
// Initialized with the host if it has a NATPortMap option enabled.
|
||||
// natManager receives signals from the network, and check on nat mappings:
|
||||
// * natManager listens to the network and adds or closes port mappings
|
||||
// as the network signals Listen() or ListenClose().
|
||||
// * closing the natManager closes the nat and its mappings.
|
||||
type natManager struct {
|
||||
net inet.Network
|
||||
natmu sync.RWMutex
|
||||
nat *inat.NAT
|
||||
|
||||
ready chan struct{} // closed once the nat is ready to process port mappings
|
||||
|
||||
syncMu sync.Mutex
|
||||
|
||||
proc goprocess.Process // natManager has a process + children. can be closed.
|
||||
}
|
||||
|
||||
func newNatManager(net inet.Network) *natManager {
|
||||
nmgr := &natManager{
|
||||
net: net,
|
||||
ready: make(chan struct{}),
|
||||
}
|
||||
|
||||
nmgr.proc = goprocess.WithTeardown(func() error {
|
||||
// on closing, unregister from network notifications.
|
||||
net.StopNotify((*nmgrNetNotifiee)(nmgr))
|
||||
return nil
|
||||
})
|
||||
|
||||
// discover the nat.
|
||||
nmgr.discoverNAT()
|
||||
return nmgr
|
||||
}
|
||||
|
||||
// Close closes the natManager, closing the underlying nat
|
||||
// and unregistering from network events.
|
||||
func (nmgr *natManager) Close() error {
|
||||
return nmgr.proc.Close()
|
||||
}
|
||||
|
||||
// Ready returns a channel which will be closed when the NAT has been found
|
||||
// and is ready to be used, or the search process is done.
|
||||
func (nmgr *natManager) Ready() <-chan struct{} {
|
||||
return nmgr.ready
|
||||
}
|
||||
|
||||
func (nmgr *natManager) discoverNAT() {
|
||||
nmgr.proc.Go(func(worker goprocess.Process) {
|
||||
// inat.DiscoverNAT blocks until the nat is found or a timeout
|
||||
// is reached. we unfortunately cannot specify timeouts-- the
|
||||
// library we're using just blocks.
|
||||
//
|
||||
// Note: on early shutdown, there may be a case where we're trying
|
||||
// to close before DiscoverNAT() returns. Since we cant cancel it
|
||||
// (library) we can choose to (1) drop the result and return early,
|
||||
// or (2) wait until it times out to exit. For now we choose (2),
|
||||
// to avoid leaking resources in a non-obvious way. the only case
|
||||
// this affects is when the daemon is being started up and _immediately_
|
||||
// asked to close. other services are also starting up, so ok to wait.
|
||||
|
||||
natInstance, err := inat.DiscoverNAT(goprocessctx.OnClosingContext(worker))
|
||||
if err != nil {
|
||||
log.Info("DiscoverNAT error:", err)
|
||||
close(nmgr.ready)
|
||||
return
|
||||
}
|
||||
|
||||
nmgr.natmu.Lock()
|
||||
nmgr.nat = natInstance
|
||||
nmgr.natmu.Unlock()
|
||||
close(nmgr.ready)
|
||||
|
||||
// wire up the nat to close when nmgr closes.
|
||||
// nmgr.proc is our parent, and waiting for us.
|
||||
nmgr.proc.AddChild(nmgr.nat.Process())
|
||||
|
||||
// sign natManager up for network notifications
|
||||
// we need to sign up here to avoid missing some notifs
|
||||
// before the NAT has been found.
|
||||
nmgr.net.Notify((*nmgrNetNotifiee)(nmgr))
|
||||
nmgr.sync()
|
||||
})
|
||||
}
|
||||
|
||||
// syncs the current NAT mappings, removing any outdated mappings and adding any
|
||||
// new mappings.
|
||||
func (nmgr *natManager) sync() {
|
||||
nat := nmgr.NAT()
|
||||
if nat == nil {
|
||||
// Nothing to do.
|
||||
return
|
||||
}
|
||||
|
||||
nmgr.proc.Go(func(_ goprocess.Process) {
|
||||
nmgr.syncMu.Lock()
|
||||
defer nmgr.syncMu.Unlock()
|
||||
|
||||
ports := map[string]map[int]bool{
|
||||
"tcp": map[int]bool{},
|
||||
"udp": map[int]bool{},
|
||||
}
|
||||
for _, maddr := range nmgr.net.ListenAddresses() {
|
||||
// Strip the IP
|
||||
maIP, rest := ma.SplitFirst(maddr)
|
||||
if maIP == nil || rest == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
switch maIP.Protocol().Code {
|
||||
case ma.P_IP6, ma.P_IP4:
|
||||
default:
|
||||
continue
|
||||
}
|
||||
|
||||
// Only bother if we're listening on a
|
||||
// unicast/unspecified IP.
|
||||
ip := net.IP(maIP.RawValue())
|
||||
if !(ip.IsGlobalUnicast() || ip.IsUnspecified()) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Extract the port/protocol
|
||||
proto, _ := ma.SplitFirst(rest)
|
||||
if proto == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
var protocol string
|
||||
switch proto.Protocol().Code {
|
||||
case ma.P_TCP:
|
||||
protocol = "tcp"
|
||||
case ma.P_UDP:
|
||||
protocol = "udp"
|
||||
default:
|
||||
continue
|
||||
}
|
||||
|
||||
port, err := strconv.ParseUint(proto.Value(), 10, 16)
|
||||
if err != nil {
|
||||
// bug in multiaddr
|
||||
panic(err)
|
||||
}
|
||||
ports[protocol][int(port)] = false
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
defer wg.Wait()
|
||||
|
||||
// Close old mappings
|
||||
for _, m := range nat.Mappings() {
|
||||
mappedPort := m.InternalPort()
|
||||
if _, ok := ports[m.Protocol()][mappedPort]; !ok {
|
||||
// No longer need this mapping.
|
||||
wg.Add(1)
|
||||
go func(m inat.Mapping) {
|
||||
defer wg.Done()
|
||||
m.Close()
|
||||
}(m)
|
||||
} else {
|
||||
// already mapped
|
||||
ports[m.Protocol()][mappedPort] = true
|
||||
}
|
||||
}
|
||||
|
||||
// Create new mappings.
|
||||
for proto, pports := range ports {
|
||||
for port, mapped := range pports {
|
||||
if mapped {
|
||||
continue
|
||||
}
|
||||
wg.Add(1)
|
||||
go func(proto string, port int) {
|
||||
defer wg.Done()
|
||||
_, err := nat.NewMapping(proto, port)
|
||||
if err != nil {
|
||||
log.Errorf("failed to port-map %s port %d: %s", proto, port, err)
|
||||
}
|
||||
}(proto, port)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// NAT returns the natManager's nat object. this may be nil, if
|
||||
// (a) the search process is still ongoing, or (b) the search process
|
||||
// found no nat. Clients must check whether the return value is nil.
|
||||
func (nmgr *natManager) NAT() *inat.NAT {
|
||||
nmgr.natmu.Lock()
|
||||
defer nmgr.natmu.Unlock()
|
||||
return nmgr.nat
|
||||
}
|
||||
|
||||
type nmgrNetNotifiee natManager
|
||||
|
||||
func (nn *nmgrNetNotifiee) natManager() *natManager {
|
||||
return (*natManager)(nn)
|
||||
}
|
||||
|
||||
func (nn *nmgrNetNotifiee) Listen(n inet.Network, addr ma.Multiaddr) {
|
||||
nn.natManager().sync()
|
||||
}
|
||||
|
||||
func (nn *nmgrNetNotifiee) ListenClose(n inet.Network, addr ma.Multiaddr) {
|
||||
nn.natManager().sync()
|
||||
}
|
||||
|
||||
func (nn *nmgrNetNotifiee) Connected(inet.Network, inet.Conn) {}
|
||||
func (nn *nmgrNetNotifiee) Disconnected(inet.Network, inet.Conn) {}
|
||||
func (nn *nmgrNetNotifiee) OpenedStream(inet.Network, inet.Stream) {}
|
||||
func (nn *nmgrNetNotifiee) ClosedStream(inet.Network, inet.Stream) {}
|
||||
+167
@@ -0,0 +1,167 @@
|
||||
package relay
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
|
||||
circuit "github.com/libp2p/go-libp2p-circuit"
|
||||
ma "github.com/multiformats/go-multiaddr"
|
||||
dns "github.com/multiformats/go-multiaddr-dns"
|
||||
manet "github.com/multiformats/go-multiaddr-net"
|
||||
)
|
||||
|
||||
// This function cleans up a relay's address set to remove private addresses and curtail
|
||||
// addrsplosion.
|
||||
func cleanupAddressSet(addrs []ma.Multiaddr) []ma.Multiaddr {
|
||||
var public, private []ma.Multiaddr
|
||||
|
||||
for _, a := range addrs {
|
||||
if isRelayAddr(a) {
|
||||
continue
|
||||
}
|
||||
|
||||
if manet.IsPublicAddr(a) || isDNSAddr(a) {
|
||||
public = append(public, a)
|
||||
continue
|
||||
}
|
||||
|
||||
// discard unroutable addrs
|
||||
if manet.IsPrivateAddr(a) {
|
||||
private = append(private, a)
|
||||
}
|
||||
}
|
||||
|
||||
if !hasAddrsplosion(public) {
|
||||
return public
|
||||
}
|
||||
|
||||
return sanitizeAddrsplodedSet(public, private)
|
||||
}
|
||||
|
||||
func isRelayAddr(a ma.Multiaddr) bool {
|
||||
isRelay := false
|
||||
|
||||
ma.ForEach(a, func(c ma.Component) bool {
|
||||
switch c.Protocol().Code {
|
||||
case circuit.P_CIRCUIT:
|
||||
isRelay = true
|
||||
return false
|
||||
default:
|
||||
return true
|
||||
}
|
||||
})
|
||||
|
||||
return isRelay
|
||||
}
|
||||
|
||||
func isDNSAddr(a ma.Multiaddr) bool {
|
||||
if first, _ := ma.SplitFirst(a); first != nil {
|
||||
switch first.Protocol().Code {
|
||||
case dns.P_DNS4, dns.P_DNS6, dns.P_DNSADDR:
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// we have addrsplosion if for some protocol we advertise multiple ports on
|
||||
// the same base address.
|
||||
func hasAddrsplosion(addrs []ma.Multiaddr) bool {
|
||||
aset := make(map[string]int)
|
||||
|
||||
for _, a := range addrs {
|
||||
key, port := addrKeyAndPort(a)
|
||||
xport, ok := aset[key]
|
||||
if ok && port != xport {
|
||||
return true
|
||||
}
|
||||
aset[key] = port
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func addrKeyAndPort(a ma.Multiaddr) (string, int) {
|
||||
var (
|
||||
key string
|
||||
port int
|
||||
)
|
||||
|
||||
ma.ForEach(a, func(c ma.Component) bool {
|
||||
switch c.Protocol().Code {
|
||||
case ma.P_TCP, ma.P_UDP:
|
||||
port = int(binary.BigEndian.Uint16(c.RawValue()))
|
||||
key += "/" + c.Protocol().Name
|
||||
default:
|
||||
val := c.Value()
|
||||
if val == "" {
|
||||
val = c.Protocol().Name
|
||||
}
|
||||
key += "/" + val
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
return key, port
|
||||
}
|
||||
|
||||
// clean up addrsplosion
|
||||
// the following heuristic is used:
|
||||
// - for each base address/protocol combination, if there are multiple ports advertised then
|
||||
// only accept the default port if present.
|
||||
// - If the default port is not present, we check for non-standard ports by tracking
|
||||
// private port bindings if present.
|
||||
// - If there is no default or private port binding, then we can't infer the correct
|
||||
// port and give up and return all addrs (for that base address)
|
||||
func sanitizeAddrsplodedSet(public, private []ma.Multiaddr) []ma.Multiaddr {
|
||||
type portAndAddr struct {
|
||||
addr ma.Multiaddr
|
||||
port int
|
||||
}
|
||||
|
||||
privports := make(map[int]struct{})
|
||||
pubaddrs := make(map[string][]portAndAddr)
|
||||
|
||||
for _, a := range private {
|
||||
_, port := addrKeyAndPort(a)
|
||||
privports[port] = struct{}{}
|
||||
}
|
||||
|
||||
for _, a := range public {
|
||||
key, port := addrKeyAndPort(a)
|
||||
pubaddrs[key] = append(pubaddrs[key], portAndAddr{addr: a, port: port})
|
||||
}
|
||||
|
||||
var result []ma.Multiaddr
|
||||
for _, pas := range pubaddrs {
|
||||
if len(pas) == 1 {
|
||||
// it's not addrsploded
|
||||
result = append(result, pas[0].addr)
|
||||
continue
|
||||
}
|
||||
|
||||
haveAddr := false
|
||||
for _, pa := range pas {
|
||||
if _, ok := privports[pa.port]; ok {
|
||||
// it matches a privately bound port, use it
|
||||
result = append(result, pa.addr)
|
||||
haveAddr = true
|
||||
continue
|
||||
}
|
||||
|
||||
if pa.port == 4001 || pa.port == 4002 {
|
||||
// it's a default port, use it
|
||||
result = append(result, pa.addr)
|
||||
haveAddr = true
|
||||
}
|
||||
}
|
||||
|
||||
if !haveAddr {
|
||||
// we weren't able to select a port; bite the bullet and use them all
|
||||
for _, pa := range pas {
|
||||
result = append(result, pa.addr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
+332
@@ -0,0 +1,332 @@
|
||||
package relay
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
basic "github.com/libp2p/go-libp2p/p2p/host/basic"
|
||||
|
||||
autonat "github.com/libp2p/go-libp2p-autonat"
|
||||
_ "github.com/libp2p/go-libp2p-circuit"
|
||||
discovery "github.com/libp2p/go-libp2p-discovery"
|
||||
inet "github.com/libp2p/go-libp2p-net"
|
||||
peer "github.com/libp2p/go-libp2p-peer"
|
||||
pstore "github.com/libp2p/go-libp2p-peerstore"
|
||||
routing "github.com/libp2p/go-libp2p-routing"
|
||||
ma "github.com/multiformats/go-multiaddr"
|
||||
manet "github.com/multiformats/go-multiaddr-net"
|
||||
)
|
||||
|
||||
const (
|
||||
RelayRendezvous = "/libp2p/relay"
|
||||
)
|
||||
|
||||
var (
|
||||
DesiredRelays = 3
|
||||
|
||||
BootDelay = 20 * time.Second
|
||||
)
|
||||
|
||||
// AutoRelay is a Host that uses relays for connectivity when a NAT is detected.
|
||||
type AutoRelay struct {
|
||||
host *basic.BasicHost
|
||||
discover discovery.Discoverer
|
||||
router routing.PeerRouting
|
||||
autonat autonat.AutoNAT
|
||||
addrsF basic.AddrsFactory
|
||||
|
||||
disconnect chan struct{}
|
||||
|
||||
mx sync.Mutex
|
||||
relays map[peer.ID]struct{}
|
||||
status autonat.NATStatus
|
||||
|
||||
cachedAddrs []ma.Multiaddr
|
||||
cachedAddrsExpiry time.Time
|
||||
}
|
||||
|
||||
func NewAutoRelay(ctx context.Context, bhost *basic.BasicHost, discover discovery.Discoverer, router routing.PeerRouting) *AutoRelay {
|
||||
ar := &AutoRelay{
|
||||
host: bhost,
|
||||
discover: discover,
|
||||
router: router,
|
||||
addrsF: bhost.AddrsFactory,
|
||||
relays: make(map[peer.ID]struct{}),
|
||||
disconnect: make(chan struct{}, 1),
|
||||
status: autonat.NATStatusUnknown,
|
||||
}
|
||||
ar.autonat = autonat.NewAutoNAT(ctx, bhost, ar.baseAddrs)
|
||||
bhost.AddrsFactory = ar.hostAddrs
|
||||
bhost.Network().Notify(ar)
|
||||
go ar.background(ctx)
|
||||
return ar
|
||||
}
|
||||
|
||||
func (ar *AutoRelay) baseAddrs() []ma.Multiaddr {
|
||||
return ar.addrsF(ar.host.AllAddrs())
|
||||
}
|
||||
|
||||
func (ar *AutoRelay) hostAddrs(addrs []ma.Multiaddr) []ma.Multiaddr {
|
||||
return ar.relayAddrs(ar.addrsF(addrs))
|
||||
}
|
||||
|
||||
func (ar *AutoRelay) background(ctx context.Context) {
|
||||
select {
|
||||
case <-time.After(autonat.AutoNATBootDelay + BootDelay):
|
||||
case <-ctx.Done():
|
||||
return
|
||||
}
|
||||
|
||||
// when true, we need to identify push
|
||||
push := false
|
||||
|
||||
for {
|
||||
wait := autonat.AutoNATRefreshInterval
|
||||
switch ar.autonat.Status() {
|
||||
case autonat.NATStatusUnknown:
|
||||
ar.mx.Lock()
|
||||
ar.status = autonat.NATStatusUnknown
|
||||
ar.mx.Unlock()
|
||||
wait = autonat.AutoNATRetryInterval
|
||||
|
||||
case autonat.NATStatusPublic:
|
||||
ar.mx.Lock()
|
||||
if ar.status != autonat.NATStatusPublic {
|
||||
push = true
|
||||
}
|
||||
ar.status = autonat.NATStatusPublic
|
||||
ar.mx.Unlock()
|
||||
|
||||
case autonat.NATStatusPrivate:
|
||||
update := ar.findRelays(ctx)
|
||||
ar.mx.Lock()
|
||||
if update || ar.status != autonat.NATStatusPrivate {
|
||||
push = true
|
||||
}
|
||||
ar.status = autonat.NATStatusPrivate
|
||||
ar.mx.Unlock()
|
||||
}
|
||||
|
||||
if push {
|
||||
ar.mx.Lock()
|
||||
ar.cachedAddrs = nil
|
||||
ar.mx.Unlock()
|
||||
push = false
|
||||
ar.host.PushIdentify()
|
||||
}
|
||||
|
||||
select {
|
||||
case <-ar.disconnect:
|
||||
push = true
|
||||
case <-time.After(wait):
|
||||
case <-ctx.Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (ar *AutoRelay) findRelays(ctx context.Context) bool {
|
||||
if ar.numRelays() >= DesiredRelays {
|
||||
return false
|
||||
}
|
||||
|
||||
update := false
|
||||
for retry := 0; retry < 5; retry++ {
|
||||
if retry > 0 {
|
||||
log.Debug("no relays connected; retrying in 30s")
|
||||
select {
|
||||
case <-time.After(30 * time.Second):
|
||||
case <-ctx.Done():
|
||||
return update
|
||||
}
|
||||
}
|
||||
|
||||
update = ar.findRelaysOnce(ctx) || update
|
||||
if ar.numRelays() > 0 {
|
||||
return update
|
||||
}
|
||||
}
|
||||
return update
|
||||
}
|
||||
|
||||
func (ar *AutoRelay) findRelaysOnce(ctx context.Context) bool {
|
||||
pis, err := ar.discoverRelays(ctx)
|
||||
if err != nil {
|
||||
log.Debugf("error discovering relays: %s", err)
|
||||
return false
|
||||
}
|
||||
log.Debugf("discovered %d relays", len(pis))
|
||||
pis = ar.selectRelays(ctx, pis)
|
||||
log.Debugf("selected %d relays", len(pis))
|
||||
|
||||
update := false
|
||||
for _, pi := range pis {
|
||||
update = ar.tryRelay(ctx, pi) || update
|
||||
if ar.numRelays() >= DesiredRelays {
|
||||
break
|
||||
}
|
||||
}
|
||||
return update
|
||||
}
|
||||
|
||||
func (ar *AutoRelay) numRelays() int {
|
||||
ar.mx.Lock()
|
||||
defer ar.mx.Unlock()
|
||||
return len(ar.relays)
|
||||
}
|
||||
|
||||
// usingRelay returns if we're currently using the given relay.
|
||||
func (ar *AutoRelay) usingRelay(p peer.ID) bool {
|
||||
ar.mx.Lock()
|
||||
defer ar.mx.Unlock()
|
||||
_, ok := ar.relays[p]
|
||||
return ok
|
||||
}
|
||||
|
||||
// addRelay adds the given relay to our set of relays.
|
||||
// returns true when we add a new relay
|
||||
func (ar *AutoRelay) tryRelay(ctx context.Context, pi pstore.PeerInfo) bool {
|
||||
if ar.usingRelay(pi.ID) {
|
||||
return false
|
||||
}
|
||||
|
||||
if !ar.connect(ctx, pi) {
|
||||
return false
|
||||
}
|
||||
|
||||
ar.mx.Lock()
|
||||
defer ar.mx.Unlock()
|
||||
|
||||
// make sure we're still connected.
|
||||
if ar.host.Network().Connectedness(pi.ID) != inet.Connected {
|
||||
return false
|
||||
}
|
||||
ar.relays[pi.ID] = struct{}{}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (ar *AutoRelay) connect(ctx context.Context, pi pstore.PeerInfo) bool {
|
||||
ctx, cancel := context.WithTimeout(ctx, 60*time.Second)
|
||||
defer cancel()
|
||||
|
||||
if len(pi.Addrs) == 0 {
|
||||
var err error
|
||||
pi, err = ar.router.FindPeer(ctx, pi.ID)
|
||||
if err != nil {
|
||||
log.Debugf("error finding relay peer %s: %s", pi.ID, err.Error())
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
err := ar.host.Connect(ctx, pi)
|
||||
if err != nil {
|
||||
log.Debugf("error connecting to relay %s: %s", pi.ID, err.Error())
|
||||
return false
|
||||
}
|
||||
|
||||
// tag the connection as very important
|
||||
ar.host.ConnManager().TagPeer(pi.ID, "relay", 42)
|
||||
return true
|
||||
}
|
||||
|
||||
func (ar *AutoRelay) discoverRelays(ctx context.Context) ([]pstore.PeerInfo, error) {
|
||||
ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
|
||||
defer cancel()
|
||||
return discovery.FindPeers(ctx, ar.discover, RelayRendezvous, 1000)
|
||||
}
|
||||
|
||||
func (ar *AutoRelay) selectRelays(ctx context.Context, pis []pstore.PeerInfo) []pstore.PeerInfo {
|
||||
// TODO better relay selection strategy; this just selects random relays
|
||||
// but we should probably use ping latency as the selection metric
|
||||
|
||||
shuffleRelays(pis)
|
||||
return pis
|
||||
}
|
||||
|
||||
// This function is computes the NATed relay addrs when our status is private:
|
||||
// - The public addrs are removed from the address set.
|
||||
// - The non-public addrs are included verbatim so that peers behind the same NAT/firewall
|
||||
// can still dial us directly.
|
||||
// - On top of those, we add the relay-specific addrs for the relays to which we are
|
||||
// connected. For each non-private relay addr, we encapsulate the p2p-circuit addr
|
||||
// through which we can be dialed.
|
||||
func (ar *AutoRelay) relayAddrs(addrs []ma.Multiaddr) []ma.Multiaddr {
|
||||
ar.mx.Lock()
|
||||
defer ar.mx.Unlock()
|
||||
|
||||
if ar.status != autonat.NATStatusPrivate {
|
||||
return addrs
|
||||
}
|
||||
|
||||
if ar.cachedAddrs != nil && time.Now().Before(ar.cachedAddrsExpiry) {
|
||||
return ar.cachedAddrs
|
||||
}
|
||||
|
||||
raddrs := make([]ma.Multiaddr, 0, 4*len(ar.relays)+4)
|
||||
|
||||
// only keep private addrs from the original addr set
|
||||
for _, addr := range addrs {
|
||||
if manet.IsPrivateAddr(addr) {
|
||||
raddrs = append(raddrs, addr)
|
||||
}
|
||||
}
|
||||
|
||||
// add relay specific addrs to the list
|
||||
for p := range ar.relays {
|
||||
addrs := cleanupAddressSet(ar.host.Peerstore().Addrs(p))
|
||||
|
||||
circuit, err := ma.NewMultiaddr(fmt.Sprintf("/p2p/%s/p2p-circuit", p.Pretty()))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
for _, addr := range addrs {
|
||||
pub := addr.Encapsulate(circuit)
|
||||
raddrs = append(raddrs, pub)
|
||||
}
|
||||
}
|
||||
|
||||
ar.cachedAddrs = raddrs
|
||||
ar.cachedAddrsExpiry = time.Now().Add(30 * time.Second)
|
||||
|
||||
return raddrs
|
||||
}
|
||||
|
||||
func shuffleRelays(pis []pstore.PeerInfo) {
|
||||
for i := range pis {
|
||||
j := rand.Intn(i + 1)
|
||||
pis[i], pis[j] = pis[j], pis[i]
|
||||
}
|
||||
}
|
||||
|
||||
// Notifee
|
||||
func (ar *AutoRelay) Listen(inet.Network, ma.Multiaddr) {}
|
||||
func (ar *AutoRelay) ListenClose(inet.Network, ma.Multiaddr) {}
|
||||
func (ar *AutoRelay) Connected(inet.Network, inet.Conn) {}
|
||||
|
||||
func (ar *AutoRelay) Disconnected(net inet.Network, c inet.Conn) {
|
||||
p := c.RemotePeer()
|
||||
|
||||
ar.mx.Lock()
|
||||
defer ar.mx.Unlock()
|
||||
|
||||
if ar.host.Network().Connectedness(p) == inet.Connected {
|
||||
// We have a second connection.
|
||||
return
|
||||
}
|
||||
|
||||
if _, ok := ar.relays[p]; ok {
|
||||
delete(ar.relays, p)
|
||||
select {
|
||||
case ar.disconnect <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (ar *AutoRelay) OpenedStream(inet.Network, inet.Stream) {}
|
||||
func (ar *AutoRelay) ClosedStream(inet.Network, inet.Stream) {}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
The relay package contains the components necessary to implement the "autorelay"
|
||||
feature.
|
||||
|
||||
Warning: the internal interfaces are unstable.
|
||||
|
||||
System Components:
|
||||
- A discovery service to discover public relays.
|
||||
- An AutoNAT client used to determine if the node is behind a NAT/firewall.
|
||||
- One or more autonat services, instances of `AutoNATServices`. These are used
|
||||
by the autonat client.
|
||||
- One or more relays, instances of `RelayHost`.
|
||||
- The AutoRelay service. This is the service that actually:
|
||||
|
||||
AutoNATService: https://github.com/libp2p/go-libp2p-autonat-svc
|
||||
AutoNAT: https://github.com/libp2p/go-libp2p-autonat
|
||||
|
||||
How it works:
|
||||
- `AutoNATService` instances are instantiated in the bootstrappers (or other
|
||||
well known publicly reachable hosts)
|
||||
- `AutoRelay`s are constructed with `libp2p.New(libp2p.Routing(makeDHT))`
|
||||
They passively discover autonat service instances and test dialability of
|
||||
their listen address set through them. When the presence of NAT is detected,
|
||||
they discover relays through the DHT, connect to some of them and begin
|
||||
advertising relay addresses. The new set of addresses is propagated to
|
||||
connected peers through the `identify/push` protocol.
|
||||
*/
|
||||
package relay
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package relay
|
||||
|
||||
import (
|
||||
logging "github.com/ipfs/go-log"
|
||||
)
|
||||
|
||||
var log = logging.Logger("autorelay")
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
package relay
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
discovery "github.com/libp2p/go-libp2p-discovery"
|
||||
ma "github.com/multiformats/go-multiaddr"
|
||||
)
|
||||
|
||||
var (
|
||||
// this is purposefully long to require some node stability before advertising as a relay
|
||||
AdvertiseBootDelay = 15 * time.Minute
|
||||
)
|
||||
|
||||
// Advertise advertises this node as a libp2p relay.
|
||||
func Advertise(ctx context.Context, advertise discovery.Advertiser) {
|
||||
go func() {
|
||||
select {
|
||||
case <-time.After(AdvertiseBootDelay):
|
||||
discovery.Advertise(ctx, advertise, RelayRendezvous)
|
||||
case <-ctx.Done():
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// Filter filters out all relay addresses.
|
||||
func Filter(addrs []ma.Multiaddr) []ma.Multiaddr {
|
||||
raddrs := make([]ma.Multiaddr, 0, len(addrs))
|
||||
for _, addr := range addrs {
|
||||
if isRelayAddr(addr) {
|
||||
continue
|
||||
}
|
||||
raddrs = append(raddrs, addr)
|
||||
}
|
||||
return raddrs
|
||||
}
|
||||
+190
@@ -0,0 +1,190 @@
|
||||
package routedhost
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
host "github.com/libp2p/go-libp2p-host"
|
||||
|
||||
logging "github.com/ipfs/go-log"
|
||||
circuit "github.com/libp2p/go-libp2p-circuit"
|
||||
ifconnmgr "github.com/libp2p/go-libp2p-interface-connmgr"
|
||||
lgbl "github.com/libp2p/go-libp2p-loggables"
|
||||
inet "github.com/libp2p/go-libp2p-net"
|
||||
peer "github.com/libp2p/go-libp2p-peer"
|
||||
pstore "github.com/libp2p/go-libp2p-peerstore"
|
||||
protocol "github.com/libp2p/go-libp2p-protocol"
|
||||
ma "github.com/multiformats/go-multiaddr"
|
||||
msmux "github.com/multiformats/go-multistream"
|
||||
)
|
||||
|
||||
var log = logging.Logger("routedhost")
|
||||
|
||||
// AddressTTL is the expiry time for our addresses.
|
||||
// We expire them quickly.
|
||||
const AddressTTL = time.Second * 10
|
||||
|
||||
// RoutedHost is a p2p Host that includes a routing system.
|
||||
// This allows the Host to find the addresses for peers when
|
||||
// it does not have them.
|
||||
type RoutedHost struct {
|
||||
host host.Host // embedded other host.
|
||||
route Routing
|
||||
}
|
||||
|
||||
type Routing interface {
|
||||
FindPeer(context.Context, peer.ID) (pstore.PeerInfo, error)
|
||||
}
|
||||
|
||||
func Wrap(h host.Host, r Routing) *RoutedHost {
|
||||
return &RoutedHost{h, r}
|
||||
}
|
||||
|
||||
// Connect ensures there is a connection between this host and the peer with
|
||||
// given peer.ID. See (host.Host).Connect for more information.
|
||||
//
|
||||
// RoutedHost's Connect differs in that if the host has no addresses for a
|
||||
// given peer, it will use its routing system to try to find some.
|
||||
func (rh *RoutedHost) Connect(ctx context.Context, pi pstore.PeerInfo) error {
|
||||
// first, check if we're already connected.
|
||||
if rh.Network().Connectedness(pi.ID) == inet.Connected {
|
||||
return nil
|
||||
}
|
||||
|
||||
// if we were given some addresses, keep + use them.
|
||||
if len(pi.Addrs) > 0 {
|
||||
rh.Peerstore().AddAddrs(pi.ID, pi.Addrs, pstore.TempAddrTTL)
|
||||
}
|
||||
|
||||
// Check if we have some addresses in our recent memory.
|
||||
addrs := rh.Peerstore().Addrs(pi.ID)
|
||||
if len(addrs) < 1 {
|
||||
// no addrs? find some with the routing system.
|
||||
var err error
|
||||
addrs, err = rh.findPeerAddrs(ctx, pi.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Issue 448: if our address set includes routed specific relay addrs,
|
||||
// we need to make sure the relay's addr itself is in the peerstore or else
|
||||
// we wont be able to dial it.
|
||||
for _, addr := range addrs {
|
||||
_, err := addr.ValueForProtocol(circuit.P_CIRCUIT)
|
||||
if err != nil {
|
||||
// not a relay address
|
||||
continue
|
||||
}
|
||||
|
||||
if addr.Protocols()[0].Code != ma.P_P2P {
|
||||
// not a routed relay specific address
|
||||
continue
|
||||
}
|
||||
|
||||
relay, _ := addr.ValueForProtocol(ma.P_P2P)
|
||||
|
||||
relayID, err := peer.IDFromString(relay)
|
||||
if err != nil {
|
||||
log.Debugf("failed to parse relay ID in address %s: %s", relay, err)
|
||||
continue
|
||||
}
|
||||
|
||||
if len(rh.Peerstore().Addrs(relayID)) > 0 {
|
||||
// we already have addrs for this relay
|
||||
continue
|
||||
}
|
||||
|
||||
relayAddrs, err := rh.findPeerAddrs(ctx, relayID)
|
||||
if err != nil {
|
||||
log.Debugf("failed to find relay %s: %s", relay, err)
|
||||
continue
|
||||
}
|
||||
|
||||
rh.Peerstore().AddAddrs(relayID, relayAddrs, pstore.TempAddrTTL)
|
||||
}
|
||||
|
||||
// if we're here, we got some addrs. let's use our wrapped host to connect.
|
||||
pi.Addrs = addrs
|
||||
return rh.host.Connect(ctx, pi)
|
||||
}
|
||||
|
||||
func (rh *RoutedHost) findPeerAddrs(ctx context.Context, id peer.ID) ([]ma.Multiaddr, error) {
|
||||
pi, err := rh.route.FindPeer(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err // couldnt find any :(
|
||||
}
|
||||
|
||||
if pi.ID != id {
|
||||
err = fmt.Errorf("routing failure: provided addrs for different peer")
|
||||
logRoutingErrDifferentPeers(ctx, id, pi.ID, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return pi.Addrs, nil
|
||||
}
|
||||
|
||||
func logRoutingErrDifferentPeers(ctx context.Context, wanted, got peer.ID, err error) {
|
||||
lm := make(lgbl.DeferredMap)
|
||||
lm["error"] = err
|
||||
lm["wantedPeer"] = func() interface{} { return wanted.Pretty() }
|
||||
lm["gotPeer"] = func() interface{} { return got.Pretty() }
|
||||
log.Event(ctx, "routingError", lm)
|
||||
}
|
||||
|
||||
func (rh *RoutedHost) ID() peer.ID {
|
||||
return rh.host.ID()
|
||||
}
|
||||
|
||||
func (rh *RoutedHost) Peerstore() pstore.Peerstore {
|
||||
return rh.host.Peerstore()
|
||||
}
|
||||
|
||||
func (rh *RoutedHost) Addrs() []ma.Multiaddr {
|
||||
return rh.host.Addrs()
|
||||
}
|
||||
|
||||
func (rh *RoutedHost) Network() inet.Network {
|
||||
return rh.host.Network()
|
||||
}
|
||||
|
||||
func (rh *RoutedHost) Mux() *msmux.MultistreamMuxer {
|
||||
return rh.host.Mux()
|
||||
}
|
||||
|
||||
func (rh *RoutedHost) SetStreamHandler(pid protocol.ID, handler inet.StreamHandler) {
|
||||
rh.host.SetStreamHandler(pid, handler)
|
||||
}
|
||||
|
||||
func (rh *RoutedHost) SetStreamHandlerMatch(pid protocol.ID, m func(string) bool, handler inet.StreamHandler) {
|
||||
rh.host.SetStreamHandlerMatch(pid, m, handler)
|
||||
}
|
||||
|
||||
func (rh *RoutedHost) RemoveStreamHandler(pid protocol.ID) {
|
||||
rh.host.RemoveStreamHandler(pid)
|
||||
}
|
||||
|
||||
func (rh *RoutedHost) NewStream(ctx context.Context, p peer.ID, pids ...protocol.ID) (inet.Stream, error) {
|
||||
// Ensure we have a connection, with peer addresses resolved by the routing system (#207)
|
||||
// It is not sufficient to let the underlying host connect, it will most likely not have
|
||||
// any addresses for the peer without any prior connections.
|
||||
// If the caller wants to prevent the host from dialing, it should use the NoDial option.
|
||||
if nodial, _ := inet.GetNoDial(ctx); !nodial {
|
||||
err := rh.Connect(ctx, pstore.PeerInfo{ID: p})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return rh.host.NewStream(ctx, p, pids...)
|
||||
}
|
||||
func (rh *RoutedHost) Close() error {
|
||||
// no need to close IpfsRouting. we dont own it.
|
||||
return rh.host.Close()
|
||||
}
|
||||
func (rh *RoutedHost) ConnManager() ifconnmgr.ConnManager {
|
||||
return rh.host.ConnManager()
|
||||
}
|
||||
|
||||
var _ (host.Host) = (*RoutedHost)(nil)
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
// Package mocknet provides a mock net.Network to test with.
|
||||
//
|
||||
// - a Mocknet has many inet.Networks
|
||||
// - a Mocknet has many Links
|
||||
// - a Link joins two inet.Networks
|
||||
// - inet.Conns and inet.Streams are created by inet.Networks
|
||||
package mocknet
|
||||
|
||||
import (
|
||||
"io"
|
||||
"time"
|
||||
|
||||
host "github.com/libp2p/go-libp2p-host"
|
||||
|
||||
ic "github.com/libp2p/go-libp2p-crypto"
|
||||
inet "github.com/libp2p/go-libp2p-net"
|
||||
peer "github.com/libp2p/go-libp2p-peer"
|
||||
pstore "github.com/libp2p/go-libp2p-peerstore"
|
||||
ma "github.com/multiformats/go-multiaddr"
|
||||
)
|
||||
|
||||
type Mocknet interface {
|
||||
|
||||
// GenPeer generates a peer and its inet.Network in the Mocknet
|
||||
GenPeer() (host.Host, error)
|
||||
|
||||
// AddPeer adds an existing peer. we need both a privkey and addr.
|
||||
// ID is derived from PrivKey
|
||||
AddPeer(ic.PrivKey, ma.Multiaddr) (host.Host, error)
|
||||
AddPeerWithPeerstore(peer.ID, pstore.Peerstore) (host.Host, error)
|
||||
|
||||
// retrieve things (with randomized iteration order)
|
||||
Peers() []peer.ID
|
||||
Net(peer.ID) inet.Network
|
||||
Nets() []inet.Network
|
||||
Host(peer.ID) host.Host
|
||||
Hosts() []host.Host
|
||||
Links() LinkMap
|
||||
LinksBetweenPeers(a, b peer.ID) []Link
|
||||
LinksBetweenNets(a, b inet.Network) []Link
|
||||
|
||||
// Links are the **ability to connect**.
|
||||
// think of Links as the physical medium.
|
||||
// For p1 and p2 to connect, a link must exist between them.
|
||||
// (this makes it possible to test dial failures, and
|
||||
// things like relaying traffic)
|
||||
LinkPeers(peer.ID, peer.ID) (Link, error)
|
||||
LinkNets(inet.Network, inet.Network) (Link, error)
|
||||
Unlink(Link) error
|
||||
UnlinkPeers(peer.ID, peer.ID) error
|
||||
UnlinkNets(inet.Network, inet.Network) error
|
||||
|
||||
// LinkDefaults are the default options that govern links
|
||||
// if they do not have thier own option set.
|
||||
SetLinkDefaults(LinkOptions)
|
||||
LinkDefaults() LinkOptions
|
||||
|
||||
// Connections are the usual. Connecting means Dialing.
|
||||
// **to succeed, peers must be linked beforehand**
|
||||
ConnectPeers(peer.ID, peer.ID) (inet.Conn, error)
|
||||
ConnectNets(inet.Network, inet.Network) (inet.Conn, error)
|
||||
DisconnectPeers(peer.ID, peer.ID) error
|
||||
DisconnectNets(inet.Network, inet.Network) error
|
||||
LinkAll() error
|
||||
ConnectAllButSelf() error
|
||||
}
|
||||
|
||||
// LinkOptions are used to change aspects of the links.
|
||||
// Sorry but they dont work yet :(
|
||||
type LinkOptions struct {
|
||||
Latency time.Duration
|
||||
Bandwidth float64 // in bytes-per-second
|
||||
// we can make these values distributions down the road.
|
||||
}
|
||||
|
||||
// Link represents the **possibility** of a connection between
|
||||
// two peers. Think of it like physical network links. Without
|
||||
// them, the peers can try and try but they won't be able to
|
||||
// connect. This allows constructing topologies where specific
|
||||
// nodes cannot talk to each other directly. :)
|
||||
type Link interface {
|
||||
Networks() []inet.Network
|
||||
Peers() []peer.ID
|
||||
|
||||
SetOptions(LinkOptions)
|
||||
Options() LinkOptions
|
||||
|
||||
// Metrics() Metrics
|
||||
}
|
||||
|
||||
// LinkMap is a 3D map to give us an easy way to track links.
|
||||
// (wow, much map. so data structure. how compose. ahhh pointer)
|
||||
type LinkMap map[string]map[string]map[Link]struct{}
|
||||
|
||||
// Printer lets you inspect things :)
|
||||
type Printer interface {
|
||||
// MocknetLinks shows the entire Mocknet's link table :)
|
||||
MocknetLinks(mn Mocknet)
|
||||
NetworkConns(ni inet.Network)
|
||||
}
|
||||
|
||||
// PrinterTo returns a Printer ready to write to w.
|
||||
func PrinterTo(w io.Writer) Printer {
|
||||
return &printer{w}
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
package mocknet
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
logging "github.com/ipfs/go-log"
|
||||
)
|
||||
|
||||
var log = logging.Logger("mocknet")
|
||||
|
||||
// WithNPeers constructs a Mocknet with N peers.
|
||||
func WithNPeers(ctx context.Context, n int) (Mocknet, error) {
|
||||
m := New(ctx)
|
||||
for i := 0; i < n; i++ {
|
||||
if _, err := m.GenPeer(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// FullMeshLinked constructs a Mocknet with full mesh of Links.
|
||||
// This means that all the peers **can** connect to each other
|
||||
// (not that they already are connected. you can use m.ConnectAll())
|
||||
func FullMeshLinked(ctx context.Context, n int) (Mocknet, error) {
|
||||
m, err := WithNPeers(ctx, n)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := m.LinkAll(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// FullMeshConnected constructs a Mocknet with full mesh of Connections.
|
||||
// This means that all the peers have dialed and are ready to talk to
|
||||
// each other.
|
||||
func FullMeshConnected(ctx context.Context, n int) (Mocknet, error) {
|
||||
m, err := FullMeshLinked(ctx, n)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = m.ConnectAllButSelf()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
+160
@@ -0,0 +1,160 @@
|
||||
package mocknet
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
"sync"
|
||||
|
||||
process "github.com/jbenet/goprocess"
|
||||
ic "github.com/libp2p/go-libp2p-crypto"
|
||||
inet "github.com/libp2p/go-libp2p-net"
|
||||
peer "github.com/libp2p/go-libp2p-peer"
|
||||
ma "github.com/multiformats/go-multiaddr"
|
||||
)
|
||||
|
||||
// conn represents one side's perspective of a
|
||||
// live connection between two peers.
|
||||
// it goes over a particular link.
|
||||
type conn struct {
|
||||
local peer.ID
|
||||
remote peer.ID
|
||||
|
||||
localAddr ma.Multiaddr
|
||||
remoteAddr ma.Multiaddr
|
||||
|
||||
localPrivKey ic.PrivKey
|
||||
remotePubKey ic.PubKey
|
||||
|
||||
net *peernet
|
||||
link *link
|
||||
rconn *conn // counterpart
|
||||
streams list.List
|
||||
proc process.Process
|
||||
stat inet.Stat
|
||||
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
func newConn(ln, rn *peernet, l *link, dir inet.Direction) *conn {
|
||||
c := &conn{net: ln, link: l}
|
||||
c.local = ln.peer
|
||||
c.remote = rn.peer
|
||||
c.stat = inet.Stat{Direction: dir}
|
||||
|
||||
c.localAddr = ln.ps.Addrs(ln.peer)[0]
|
||||
c.remoteAddr = rn.ps.Addrs(rn.peer)[0]
|
||||
|
||||
c.localPrivKey = ln.ps.PrivKey(ln.peer)
|
||||
c.remotePubKey = rn.ps.PubKey(rn.peer)
|
||||
|
||||
c.proc = process.WithTeardown(c.teardown)
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *conn) Close() error {
|
||||
return c.proc.Close()
|
||||
}
|
||||
|
||||
func (c *conn) teardown() error {
|
||||
for _, s := range c.allStreams() {
|
||||
s.Reset()
|
||||
}
|
||||
c.net.removeConn(c)
|
||||
c.net.notifyAll(func(n inet.Notifiee) {
|
||||
n.Disconnected(c.net, c)
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *conn) addStream(s *stream) {
|
||||
c.Lock()
|
||||
s.conn = c
|
||||
c.streams.PushBack(s)
|
||||
c.Unlock()
|
||||
}
|
||||
|
||||
func (c *conn) removeStream(s *stream) {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
for e := c.streams.Front(); e != nil; e = e.Next() {
|
||||
if s == e.Value {
|
||||
c.streams.Remove(e)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *conn) allStreams() []inet.Stream {
|
||||
c.RLock()
|
||||
defer c.RUnlock()
|
||||
|
||||
strs := make([]inet.Stream, 0, c.streams.Len())
|
||||
for e := c.streams.Front(); e != nil; e = e.Next() {
|
||||
s := e.Value.(*stream)
|
||||
strs = append(strs, s)
|
||||
}
|
||||
return strs
|
||||
}
|
||||
|
||||
func (c *conn) remoteOpenedStream(s *stream) {
|
||||
c.addStream(s)
|
||||
c.net.handleNewStream(s)
|
||||
c.net.notifyAll(func(n inet.Notifiee) {
|
||||
n.OpenedStream(c.net, s)
|
||||
})
|
||||
}
|
||||
|
||||
func (c *conn) openStream() *stream {
|
||||
sl, sr := c.link.newStreamPair()
|
||||
c.addStream(sl)
|
||||
c.net.notifyAll(func(n inet.Notifiee) {
|
||||
n.OpenedStream(c.net, sl)
|
||||
})
|
||||
c.rconn.remoteOpenedStream(sr)
|
||||
return sl
|
||||
}
|
||||
|
||||
func (c *conn) NewStream() (inet.Stream, error) {
|
||||
log.Debugf("Conn.NewStreamWithProtocol: %s --> %s", c.local, c.remote)
|
||||
|
||||
s := c.openStream()
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func (c *conn) GetStreams() []inet.Stream {
|
||||
return c.allStreams()
|
||||
}
|
||||
|
||||
// LocalMultiaddr is the Multiaddr on this side
|
||||
func (c *conn) LocalMultiaddr() ma.Multiaddr {
|
||||
return c.localAddr
|
||||
}
|
||||
|
||||
// LocalPeer is the Peer on our side of the connection
|
||||
func (c *conn) LocalPeer() peer.ID {
|
||||
return c.local
|
||||
}
|
||||
|
||||
// LocalPrivateKey is the private key of the peer on our side.
|
||||
func (c *conn) LocalPrivateKey() ic.PrivKey {
|
||||
return c.localPrivKey
|
||||
}
|
||||
|
||||
// RemoteMultiaddr is the Multiaddr on the remote side
|
||||
func (c *conn) RemoteMultiaddr() ma.Multiaddr {
|
||||
return c.remoteAddr
|
||||
}
|
||||
|
||||
// RemotePeer is the Peer on the remote side
|
||||
func (c *conn) RemotePeer() peer.ID {
|
||||
return c.remote
|
||||
}
|
||||
|
||||
// RemotePublicKey is the private key of the peer on our side.
|
||||
func (c *conn) RemotePublicKey() ic.PubKey {
|
||||
return c.remotePubKey
|
||||
}
|
||||
|
||||
// Stat returns metadata about the connection
|
||||
func (c *conn) Stat() inet.Stat {
|
||||
return c.stat
|
||||
}
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
package mocknet
|
||||
|
||||
import (
|
||||
// "fmt"
|
||||
"io"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
inet "github.com/libp2p/go-libp2p-net"
|
||||
peer "github.com/libp2p/go-libp2p-peer"
|
||||
)
|
||||
|
||||
// link implements mocknet.Link
|
||||
// and, for simplicity, inet.Conn
|
||||
type link struct {
|
||||
mock *mocknet
|
||||
nets []*peernet
|
||||
opts LinkOptions
|
||||
ratelimiter *RateLimiter
|
||||
// this could have addresses on both sides.
|
||||
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
func newLink(mn *mocknet, opts LinkOptions) *link {
|
||||
l := &link{mock: mn,
|
||||
opts: opts,
|
||||
ratelimiter: NewRateLimiter(opts.Bandwidth)}
|
||||
return l
|
||||
}
|
||||
|
||||
func (l *link) newConnPair(dialer *peernet) (*conn, *conn) {
|
||||
l.RLock()
|
||||
defer l.RUnlock()
|
||||
|
||||
c1 := newConn(l.nets[0], l.nets[1], l, inet.DirOutbound)
|
||||
c2 := newConn(l.nets[1], l.nets[0], l, inet.DirInbound)
|
||||
c1.rconn = c2
|
||||
c2.rconn = c1
|
||||
|
||||
if dialer == c1.net {
|
||||
return c1, c2
|
||||
}
|
||||
return c2, c1
|
||||
}
|
||||
|
||||
func (l *link) newStreamPair() (*stream, *stream) {
|
||||
ra, wb := io.Pipe()
|
||||
rb, wa := io.Pipe()
|
||||
|
||||
sa := NewStream(wa, ra, inet.DirOutbound)
|
||||
sb := NewStream(wb, rb, inet.DirInbound)
|
||||
return sa, sb
|
||||
}
|
||||
|
||||
func (l *link) Networks() []inet.Network {
|
||||
l.RLock()
|
||||
defer l.RUnlock()
|
||||
|
||||
cp := make([]inet.Network, len(l.nets))
|
||||
for i, n := range l.nets {
|
||||
cp[i] = n
|
||||
}
|
||||
return cp
|
||||
}
|
||||
|
||||
func (l *link) Peers() []peer.ID {
|
||||
l.RLock()
|
||||
defer l.RUnlock()
|
||||
|
||||
cp := make([]peer.ID, len(l.nets))
|
||||
for i, n := range l.nets {
|
||||
cp[i] = n.peer
|
||||
}
|
||||
return cp
|
||||
}
|
||||
|
||||
func (l *link) SetOptions(o LinkOptions) {
|
||||
l.Lock()
|
||||
defer l.Unlock()
|
||||
l.opts = o
|
||||
l.ratelimiter.UpdateBandwidth(l.opts.Bandwidth)
|
||||
}
|
||||
|
||||
func (l *link) Options() LinkOptions {
|
||||
l.RLock()
|
||||
defer l.RUnlock()
|
||||
return l.opts
|
||||
}
|
||||
|
||||
func (l *link) GetLatency() time.Duration {
|
||||
l.RLock()
|
||||
defer l.RUnlock()
|
||||
return l.opts.Latency
|
||||
}
|
||||
|
||||
func (l *link) RateLimit(dataSize int) time.Duration {
|
||||
return l.ratelimiter.Limit(dataSize)
|
||||
}
|
||||
+396
@@ -0,0 +1,396 @@
|
||||
package mocknet
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"sort"
|
||||
"sync"
|
||||
|
||||
host "github.com/libp2p/go-libp2p-host"
|
||||
bhost "github.com/libp2p/go-libp2p/p2p/host/basic"
|
||||
|
||||
"github.com/jbenet/goprocess"
|
||||
goprocessctx "github.com/jbenet/goprocess/context"
|
||||
ic "github.com/libp2p/go-libp2p-crypto"
|
||||
inet "github.com/libp2p/go-libp2p-net"
|
||||
p2putil "github.com/libp2p/go-libp2p-netutil"
|
||||
peer "github.com/libp2p/go-libp2p-peer"
|
||||
pstore "github.com/libp2p/go-libp2p-peerstore"
|
||||
pstoremem "github.com/libp2p/go-libp2p-peerstore/pstoremem"
|
||||
ma "github.com/multiformats/go-multiaddr"
|
||||
)
|
||||
|
||||
// IP6 range that gets blackholed (in case our traffic ever makes it out onto
|
||||
// the internet).
|
||||
var blackholeIP6 = net.ParseIP("100::")
|
||||
|
||||
// mocknet implements mocknet.Mocknet
|
||||
type mocknet struct {
|
||||
nets map[peer.ID]*peernet
|
||||
hosts map[peer.ID]*bhost.BasicHost
|
||||
|
||||
// links make it possible to connect two peers.
|
||||
// think of links as the physical medium.
|
||||
// usually only one, but there could be multiple
|
||||
// **links are shared between peers**
|
||||
links map[peer.ID]map[peer.ID]map[*link]struct{}
|
||||
|
||||
linkDefaults LinkOptions
|
||||
|
||||
proc goprocess.Process // for Context closing
|
||||
ctx context.Context
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
func New(ctx context.Context) Mocknet {
|
||||
return &mocknet{
|
||||
nets: map[peer.ID]*peernet{},
|
||||
hosts: map[peer.ID]*bhost.BasicHost{},
|
||||
links: map[peer.ID]map[peer.ID]map[*link]struct{}{},
|
||||
proc: goprocessctx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
}
|
||||
}
|
||||
|
||||
func (mn *mocknet) GenPeer() (host.Host, error) {
|
||||
sk, err := p2putil.RandTestBogusPrivateKey()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
id, err := peer.IDFromPrivateKey(sk)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
suffix := id
|
||||
if len(id) > 8 {
|
||||
suffix = id[len(id)-8:]
|
||||
}
|
||||
ip := append(net.IP{}, blackholeIP6...)
|
||||
copy(ip[net.IPv6len-len(suffix):], suffix)
|
||||
a, err := ma.NewMultiaddr(fmt.Sprintf("/ip6/%s/tcp/4242", ip))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create test multiaddr: %s", err)
|
||||
}
|
||||
|
||||
h, err := mn.AddPeer(sk, a)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return h, nil
|
||||
}
|
||||
|
||||
func (mn *mocknet) AddPeer(k ic.PrivKey, a ma.Multiaddr) (host.Host, error) {
|
||||
p, err := peer.IDFromPublicKey(k.GetPublic())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ps := pstoremem.NewPeerstore()
|
||||
ps.AddAddr(p, a, pstore.PermanentAddrTTL)
|
||||
ps.AddPrivKey(p, k)
|
||||
ps.AddPubKey(p, k.GetPublic())
|
||||
|
||||
return mn.AddPeerWithPeerstore(p, ps)
|
||||
}
|
||||
|
||||
func (mn *mocknet) AddPeerWithPeerstore(p peer.ID, ps pstore.Peerstore) (host.Host, error) {
|
||||
n, err := newPeernet(mn.ctx, mn, p, ps)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
opts := &bhost.HostOpts{
|
||||
NegotiationTimeout: -1,
|
||||
}
|
||||
|
||||
h, err := bhost.NewHost(mn.ctx, n, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mn.proc.AddChild(n.proc)
|
||||
|
||||
mn.Lock()
|
||||
mn.nets[n.peer] = n
|
||||
mn.hosts[n.peer] = h
|
||||
mn.Unlock()
|
||||
return h, nil
|
||||
}
|
||||
|
||||
func (mn *mocknet) Peers() []peer.ID {
|
||||
mn.Lock()
|
||||
defer mn.Unlock()
|
||||
|
||||
cp := make([]peer.ID, 0, len(mn.nets))
|
||||
for _, n := range mn.nets {
|
||||
cp = append(cp, n.peer)
|
||||
}
|
||||
sort.Sort(peer.IDSlice(cp))
|
||||
return cp
|
||||
}
|
||||
|
||||
func (mn *mocknet) Host(pid peer.ID) host.Host {
|
||||
mn.Lock()
|
||||
host := mn.hosts[pid]
|
||||
mn.Unlock()
|
||||
return host
|
||||
}
|
||||
|
||||
func (mn *mocknet) Net(pid peer.ID) inet.Network {
|
||||
mn.Lock()
|
||||
n := mn.nets[pid]
|
||||
mn.Unlock()
|
||||
return n
|
||||
}
|
||||
|
||||
func (mn *mocknet) Hosts() []host.Host {
|
||||
mn.Lock()
|
||||
defer mn.Unlock()
|
||||
|
||||
cp := make([]host.Host, 0, len(mn.hosts))
|
||||
for _, h := range mn.hosts {
|
||||
cp = append(cp, h)
|
||||
}
|
||||
|
||||
sort.Sort(hostSlice(cp))
|
||||
return cp
|
||||
}
|
||||
|
||||
func (mn *mocknet) Nets() []inet.Network {
|
||||
mn.Lock()
|
||||
defer mn.Unlock()
|
||||
|
||||
cp := make([]inet.Network, 0, len(mn.nets))
|
||||
for _, n := range mn.nets {
|
||||
cp = append(cp, n)
|
||||
}
|
||||
sort.Sort(netSlice(cp))
|
||||
return cp
|
||||
}
|
||||
|
||||
// Links returns a copy of the internal link state map.
|
||||
// (wow, much map. so data structure. how compose. ahhh pointer)
|
||||
func (mn *mocknet) Links() LinkMap {
|
||||
mn.Lock()
|
||||
defer mn.Unlock()
|
||||
|
||||
links := map[string]map[string]map[Link]struct{}{}
|
||||
for p1, lm := range mn.links {
|
||||
sp1 := string(p1)
|
||||
links[sp1] = map[string]map[Link]struct{}{}
|
||||
for p2, ls := range lm {
|
||||
sp2 := string(p2)
|
||||
links[sp1][sp2] = map[Link]struct{}{}
|
||||
for l := range ls {
|
||||
links[sp1][sp2][l] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
return links
|
||||
}
|
||||
|
||||
func (mn *mocknet) LinkAll() error {
|
||||
nets := mn.Nets()
|
||||
for _, n1 := range nets {
|
||||
for _, n2 := range nets {
|
||||
if _, err := mn.LinkNets(n1, n2); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mn *mocknet) LinkPeers(p1, p2 peer.ID) (Link, error) {
|
||||
mn.Lock()
|
||||
n1 := mn.nets[p1]
|
||||
n2 := mn.nets[p2]
|
||||
mn.Unlock()
|
||||
|
||||
if n1 == nil {
|
||||
return nil, fmt.Errorf("network for p1 not in mocknet")
|
||||
}
|
||||
|
||||
if n2 == nil {
|
||||
return nil, fmt.Errorf("network for p2 not in mocknet")
|
||||
}
|
||||
|
||||
return mn.LinkNets(n1, n2)
|
||||
}
|
||||
|
||||
func (mn *mocknet) validate(n inet.Network) (*peernet, error) {
|
||||
// WARNING: assumes locks acquired
|
||||
|
||||
nr, ok := n.(*peernet)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("Network not supported (use mock package nets only)")
|
||||
}
|
||||
|
||||
if _, found := mn.nets[nr.peer]; !found {
|
||||
return nil, fmt.Errorf("Network not on mocknet. is it from another mocknet?")
|
||||
}
|
||||
|
||||
return nr, nil
|
||||
}
|
||||
|
||||
func (mn *mocknet) LinkNets(n1, n2 inet.Network) (Link, error) {
|
||||
mn.Lock()
|
||||
n1r, err1 := mn.validate(n1)
|
||||
n2r, err2 := mn.validate(n2)
|
||||
ld := mn.linkDefaults
|
||||
mn.Unlock()
|
||||
|
||||
if err1 != nil {
|
||||
return nil, err1
|
||||
}
|
||||
if err2 != nil {
|
||||
return nil, err2
|
||||
}
|
||||
|
||||
l := newLink(mn, ld)
|
||||
l.nets = append(l.nets, n1r, n2r)
|
||||
mn.addLink(l)
|
||||
return l, nil
|
||||
}
|
||||
|
||||
func (mn *mocknet) Unlink(l2 Link) error {
|
||||
|
||||
l, ok := l2.(*link)
|
||||
if !ok {
|
||||
return fmt.Errorf("only links from mocknet are supported")
|
||||
}
|
||||
|
||||
mn.removeLink(l)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mn *mocknet) UnlinkPeers(p1, p2 peer.ID) error {
|
||||
ls := mn.LinksBetweenPeers(p1, p2)
|
||||
if ls == nil {
|
||||
return fmt.Errorf("no link between p1 and p2")
|
||||
}
|
||||
|
||||
for _, l := range ls {
|
||||
if err := mn.Unlink(l); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mn *mocknet) UnlinkNets(n1, n2 inet.Network) error {
|
||||
return mn.UnlinkPeers(n1.LocalPeer(), n2.LocalPeer())
|
||||
}
|
||||
|
||||
// get from the links map. and lazily contruct.
|
||||
func (mn *mocknet) linksMapGet(p1, p2 peer.ID) map[*link]struct{} {
|
||||
|
||||
l1, found := mn.links[p1]
|
||||
if !found {
|
||||
mn.links[p1] = map[peer.ID]map[*link]struct{}{}
|
||||
l1 = mn.links[p1] // so we make sure it's there.
|
||||
}
|
||||
|
||||
l2, found := l1[p2]
|
||||
if !found {
|
||||
m := map[*link]struct{}{}
|
||||
l1[p2] = m
|
||||
l2 = l1[p2]
|
||||
}
|
||||
|
||||
return l2
|
||||
}
|
||||
|
||||
func (mn *mocknet) addLink(l *link) {
|
||||
mn.Lock()
|
||||
defer mn.Unlock()
|
||||
|
||||
n1, n2 := l.nets[0], l.nets[1]
|
||||
mn.linksMapGet(n1.peer, n2.peer)[l] = struct{}{}
|
||||
mn.linksMapGet(n2.peer, n1.peer)[l] = struct{}{}
|
||||
}
|
||||
|
||||
func (mn *mocknet) removeLink(l *link) {
|
||||
mn.Lock()
|
||||
defer mn.Unlock()
|
||||
|
||||
n1, n2 := l.nets[0], l.nets[1]
|
||||
delete(mn.linksMapGet(n1.peer, n2.peer), l)
|
||||
delete(mn.linksMapGet(n2.peer, n1.peer), l)
|
||||
}
|
||||
|
||||
func (mn *mocknet) ConnectAllButSelf() error {
|
||||
nets := mn.Nets()
|
||||
for _, n1 := range nets {
|
||||
for _, n2 := range nets {
|
||||
if n1 == n2 {
|
||||
continue
|
||||
}
|
||||
|
||||
if _, err := mn.ConnectNets(n1, n2); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mn *mocknet) ConnectPeers(a, b peer.ID) (inet.Conn, error) {
|
||||
return mn.Net(a).DialPeer(mn.ctx, b)
|
||||
}
|
||||
|
||||
func (mn *mocknet) ConnectNets(a, b inet.Network) (inet.Conn, error) {
|
||||
return a.DialPeer(mn.ctx, b.LocalPeer())
|
||||
}
|
||||
|
||||
func (mn *mocknet) DisconnectPeers(p1, p2 peer.ID) error {
|
||||
return mn.Net(p1).ClosePeer(p2)
|
||||
}
|
||||
|
||||
func (mn *mocknet) DisconnectNets(n1, n2 inet.Network) error {
|
||||
return n1.ClosePeer(n2.LocalPeer())
|
||||
}
|
||||
|
||||
func (mn *mocknet) LinksBetweenPeers(p1, p2 peer.ID) []Link {
|
||||
mn.Lock()
|
||||
defer mn.Unlock()
|
||||
|
||||
ls2 := mn.linksMapGet(p1, p2)
|
||||
cp := make([]Link, 0, len(ls2))
|
||||
for l := range ls2 {
|
||||
cp = append(cp, l)
|
||||
}
|
||||
return cp
|
||||
}
|
||||
|
||||
func (mn *mocknet) LinksBetweenNets(n1, n2 inet.Network) []Link {
|
||||
return mn.LinksBetweenPeers(n1.LocalPeer(), n2.LocalPeer())
|
||||
}
|
||||
|
||||
func (mn *mocknet) SetLinkDefaults(o LinkOptions) {
|
||||
mn.Lock()
|
||||
mn.linkDefaults = o
|
||||
mn.Unlock()
|
||||
}
|
||||
|
||||
func (mn *mocknet) LinkDefaults() LinkOptions {
|
||||
mn.Lock()
|
||||
defer mn.Unlock()
|
||||
return mn.linkDefaults
|
||||
}
|
||||
|
||||
// netSlice for sorting by peer
|
||||
type netSlice []inet.Network
|
||||
|
||||
func (es netSlice) Len() int { return len(es) }
|
||||
func (es netSlice) Swap(i, j int) { es[i], es[j] = es[j], es[i] }
|
||||
func (es netSlice) Less(i, j int) bool { return string(es[i].LocalPeer()) < string(es[j].LocalPeer()) }
|
||||
|
||||
// hostSlice for sorting by peer
|
||||
type hostSlice []host.Host
|
||||
|
||||
func (es hostSlice) Len() int { return len(es) }
|
||||
func (es hostSlice) Swap(i, j int) { es[i], es[j] = es[j], es[i] }
|
||||
func (es hostSlice) Less(i, j int) bool { return string(es[i].ID()) < string(es[j].ID()) }
|
||||
+385
@@ -0,0 +1,385 @@
|
||||
package mocknet
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"sync"
|
||||
|
||||
"github.com/jbenet/goprocess"
|
||||
goprocessctx "github.com/jbenet/goprocess/context"
|
||||
inet "github.com/libp2p/go-libp2p-net"
|
||||
peer "github.com/libp2p/go-libp2p-peer"
|
||||
pstore "github.com/libp2p/go-libp2p-peerstore"
|
||||
ma "github.com/multiformats/go-multiaddr"
|
||||
)
|
||||
|
||||
// peernet implements inet.Network
|
||||
type peernet struct {
|
||||
mocknet *mocknet // parent
|
||||
|
||||
peer peer.ID
|
||||
ps pstore.Peerstore
|
||||
|
||||
// conns are actual live connections between peers.
|
||||
// many conns could run over each link.
|
||||
// **conns are NOT shared between peers**
|
||||
connsByPeer map[peer.ID]map[*conn]struct{}
|
||||
connsByLink map[*link]map[*conn]struct{}
|
||||
|
||||
// implement inet.Network
|
||||
streamHandler inet.StreamHandler
|
||||
connHandler inet.ConnHandler
|
||||
|
||||
notifmu sync.Mutex
|
||||
notifs map[inet.Notifiee]struct{}
|
||||
|
||||
proc goprocess.Process
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
// newPeernet constructs a new peernet
|
||||
func newPeernet(ctx context.Context, m *mocknet, p peer.ID, ps pstore.Peerstore) (*peernet, error) {
|
||||
|
||||
n := &peernet{
|
||||
mocknet: m,
|
||||
peer: p,
|
||||
ps: ps,
|
||||
|
||||
connsByPeer: map[peer.ID]map[*conn]struct{}{},
|
||||
connsByLink: map[*link]map[*conn]struct{}{},
|
||||
|
||||
notifs: make(map[inet.Notifiee]struct{}),
|
||||
}
|
||||
|
||||
n.proc = goprocessctx.WithContextAndTeardown(ctx, n.teardown)
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (pn *peernet) teardown() error {
|
||||
|
||||
// close the connections
|
||||
for _, c := range pn.allConns() {
|
||||
c.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// allConns returns all the connections between this peer and others
|
||||
func (pn *peernet) allConns() []*conn {
|
||||
pn.RLock()
|
||||
var cs []*conn
|
||||
for _, csl := range pn.connsByPeer {
|
||||
for c := range csl {
|
||||
cs = append(cs, c)
|
||||
}
|
||||
}
|
||||
pn.RUnlock()
|
||||
return cs
|
||||
}
|
||||
|
||||
// Close calls the ContextCloser func
|
||||
func (pn *peernet) Close() error {
|
||||
return pn.proc.Close()
|
||||
}
|
||||
|
||||
func (pn *peernet) Peerstore() pstore.Peerstore {
|
||||
return pn.ps
|
||||
}
|
||||
|
||||
func (pn *peernet) String() string {
|
||||
return fmt.Sprintf("<mock.peernet %s - %d conns>", pn.peer, len(pn.allConns()))
|
||||
}
|
||||
|
||||
// handleNewStream is an internal function to trigger the client's handler
|
||||
func (pn *peernet) handleNewStream(s inet.Stream) {
|
||||
pn.RLock()
|
||||
handler := pn.streamHandler
|
||||
pn.RUnlock()
|
||||
if handler != nil {
|
||||
go handler(s)
|
||||
}
|
||||
}
|
||||
|
||||
// handleNewConn is an internal function to trigger the client's handler
|
||||
func (pn *peernet) handleNewConn(c inet.Conn) {
|
||||
pn.RLock()
|
||||
handler := pn.connHandler
|
||||
pn.RUnlock()
|
||||
if handler != nil {
|
||||
go handler(c)
|
||||
}
|
||||
}
|
||||
|
||||
// DialPeer attempts to establish a connection to a given peer.
|
||||
// Respects the context.
|
||||
func (pn *peernet) DialPeer(ctx context.Context, p peer.ID) (inet.Conn, error) {
|
||||
return pn.connect(p)
|
||||
}
|
||||
|
||||
func (pn *peernet) connect(p peer.ID) (*conn, error) {
|
||||
if p == pn.peer {
|
||||
return nil, fmt.Errorf("attempted to dial self %s", p)
|
||||
}
|
||||
|
||||
// first, check if we already have live connections
|
||||
pn.RLock()
|
||||
cs, found := pn.connsByPeer[p]
|
||||
if found && len(cs) > 0 {
|
||||
var chosen *conn
|
||||
for c := range cs { // because cs is a map
|
||||
chosen = c // select first
|
||||
break
|
||||
}
|
||||
pn.RUnlock()
|
||||
return chosen, nil
|
||||
}
|
||||
pn.RUnlock()
|
||||
|
||||
log.Debugf("%s (newly) dialing %s", pn.peer, p)
|
||||
|
||||
// ok, must create a new connection. we need a link
|
||||
links := pn.mocknet.LinksBetweenPeers(pn.peer, p)
|
||||
if len(links) < 1 {
|
||||
return nil, fmt.Errorf("%s cannot connect to %s", pn.peer, p)
|
||||
}
|
||||
|
||||
// if many links found, how do we select? for now, randomly...
|
||||
// this would be an interesting place to test logic that can measure
|
||||
// links (network interfaces) and select properly
|
||||
l := links[rand.Intn(len(links))]
|
||||
|
||||
log.Debugf("%s dialing %s openingConn", pn.peer, p)
|
||||
// create a new connection with link
|
||||
c := pn.openConn(p, l.(*link))
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (pn *peernet) openConn(r peer.ID, l *link) *conn {
|
||||
lc, rc := l.newConnPair(pn)
|
||||
log.Debugf("%s opening connection to %s", pn.LocalPeer(), lc.RemotePeer())
|
||||
pn.addConn(lc)
|
||||
pn.notifyAll(func(n inet.Notifiee) {
|
||||
n.Connected(pn, lc)
|
||||
})
|
||||
rc.net.remoteOpenedConn(rc)
|
||||
return lc
|
||||
}
|
||||
|
||||
func (pn *peernet) remoteOpenedConn(c *conn) {
|
||||
log.Debugf("%s accepting connection from %s", pn.LocalPeer(), c.RemotePeer())
|
||||
pn.addConn(c)
|
||||
pn.handleNewConn(c)
|
||||
pn.notifyAll(func(n inet.Notifiee) {
|
||||
n.Connected(pn, c)
|
||||
})
|
||||
}
|
||||
|
||||
// addConn constructs and adds a connection
|
||||
// to given remote peer over given link
|
||||
func (pn *peernet) addConn(c *conn) {
|
||||
pn.Lock()
|
||||
defer pn.Unlock()
|
||||
|
||||
cs, found := pn.connsByPeer[c.RemotePeer()]
|
||||
if !found {
|
||||
cs = map[*conn]struct{}{}
|
||||
pn.connsByPeer[c.RemotePeer()] = cs
|
||||
}
|
||||
pn.connsByPeer[c.RemotePeer()][c] = struct{}{}
|
||||
|
||||
cs, found = pn.connsByLink[c.link]
|
||||
if !found {
|
||||
cs = map[*conn]struct{}{}
|
||||
pn.connsByLink[c.link] = cs
|
||||
}
|
||||
pn.connsByLink[c.link][c] = struct{}{}
|
||||
}
|
||||
|
||||
// removeConn removes a given conn
|
||||
func (pn *peernet) removeConn(c *conn) {
|
||||
pn.Lock()
|
||||
defer pn.Unlock()
|
||||
|
||||
cs, found := pn.connsByLink[c.link]
|
||||
if !found || len(cs) < 1 {
|
||||
panic(fmt.Sprintf("attempting to remove a conn that doesnt exist %p", c.link))
|
||||
}
|
||||
delete(cs, c)
|
||||
|
||||
cs, found = pn.connsByPeer[c.remote]
|
||||
if !found {
|
||||
panic(fmt.Sprintf("attempting to remove a conn that doesnt exist %v", c.remote))
|
||||
}
|
||||
delete(cs, c)
|
||||
}
|
||||
|
||||
// Process returns the network's Process
|
||||
func (pn *peernet) Process() goprocess.Process {
|
||||
return pn.proc
|
||||
}
|
||||
|
||||
// LocalPeer the network's LocalPeer
|
||||
func (pn *peernet) LocalPeer() peer.ID {
|
||||
return pn.peer
|
||||
}
|
||||
|
||||
// Peers returns the connected peers
|
||||
func (pn *peernet) Peers() []peer.ID {
|
||||
pn.RLock()
|
||||
defer pn.RUnlock()
|
||||
|
||||
peers := make([]peer.ID, 0, len(pn.connsByPeer))
|
||||
for _, cs := range pn.connsByPeer {
|
||||
for c := range cs {
|
||||
peers = append(peers, c.remote)
|
||||
break
|
||||
}
|
||||
}
|
||||
return peers
|
||||
}
|
||||
|
||||
// Conns returns all the connections of this peer
|
||||
func (pn *peernet) Conns() []inet.Conn {
|
||||
pn.RLock()
|
||||
defer pn.RUnlock()
|
||||
|
||||
out := make([]inet.Conn, 0, len(pn.connsByPeer))
|
||||
for _, cs := range pn.connsByPeer {
|
||||
for c := range cs {
|
||||
out = append(out, c)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func (pn *peernet) ConnsToPeer(p peer.ID) []inet.Conn {
|
||||
pn.RLock()
|
||||
defer pn.RUnlock()
|
||||
|
||||
cs, found := pn.connsByPeer[p]
|
||||
if !found || len(cs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
var cs2 []inet.Conn
|
||||
for c := range cs {
|
||||
cs2 = append(cs2, c)
|
||||
}
|
||||
return cs2
|
||||
}
|
||||
|
||||
// ClosePeer connections to peer
|
||||
func (pn *peernet) ClosePeer(p peer.ID) error {
|
||||
pn.RLock()
|
||||
cs, found := pn.connsByPeer[p]
|
||||
if !found {
|
||||
pn.RUnlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
var conns []*conn
|
||||
for c := range cs {
|
||||
conns = append(conns, c)
|
||||
}
|
||||
pn.RUnlock()
|
||||
for _, c := range conns {
|
||||
c.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// BandwidthTotals returns the total amount of bandwidth transferred
|
||||
func (pn *peernet) BandwidthTotals() (in uint64, out uint64) {
|
||||
// need to implement this. probably best to do it in swarm this time.
|
||||
// need a "metrics" object
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
// Listen tells the network to start listening on given multiaddrs.
|
||||
func (pn *peernet) Listen(addrs ...ma.Multiaddr) error {
|
||||
pn.Peerstore().AddAddrs(pn.LocalPeer(), addrs, pstore.PermanentAddrTTL)
|
||||
return nil
|
||||
}
|
||||
|
||||
// ListenAddresses returns a list of addresses at which this network listens.
|
||||
func (pn *peernet) ListenAddresses() []ma.Multiaddr {
|
||||
return pn.Peerstore().Addrs(pn.LocalPeer())
|
||||
}
|
||||
|
||||
// InterfaceListenAddresses returns a list of addresses at which this network
|
||||
// listens. It expands "any interface" addresses (/ip4/0.0.0.0, /ip6/::) to
|
||||
// use the known local interfaces.
|
||||
func (pn *peernet) InterfaceListenAddresses() ([]ma.Multiaddr, error) {
|
||||
return pn.ListenAddresses(), nil
|
||||
}
|
||||
|
||||
// Connectedness returns a state signaling connection capabilities
|
||||
// For now only returns Connecter || NotConnected. Expand into more later.
|
||||
func (pn *peernet) Connectedness(p peer.ID) inet.Connectedness {
|
||||
pn.Lock()
|
||||
defer pn.Unlock()
|
||||
|
||||
cs, found := pn.connsByPeer[p]
|
||||
if found && len(cs) > 0 {
|
||||
return inet.Connected
|
||||
}
|
||||
return inet.NotConnected
|
||||
}
|
||||
|
||||
// NewStream returns a new stream to given peer p.
|
||||
// If there is no connection to p, attempts to create one.
|
||||
func (pn *peernet) NewStream(ctx context.Context, p peer.ID) (inet.Stream, error) {
|
||||
c, err := pn.DialPeer(ctx, p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.NewStream()
|
||||
}
|
||||
|
||||
// SetStreamHandler sets the new stream handler on the Network.
|
||||
// This operation is threadsafe.
|
||||
func (pn *peernet) SetStreamHandler(h inet.StreamHandler) {
|
||||
pn.Lock()
|
||||
pn.streamHandler = h
|
||||
pn.Unlock()
|
||||
}
|
||||
|
||||
// SetConnHandler sets the new conn handler on the Network.
|
||||
// This operation is threadsafe.
|
||||
func (pn *peernet) SetConnHandler(h inet.ConnHandler) {
|
||||
pn.Lock()
|
||||
pn.connHandler = h
|
||||
pn.Unlock()
|
||||
}
|
||||
|
||||
// Notify signs up Notifiee to receive signals when events happen
|
||||
func (pn *peernet) Notify(f inet.Notifiee) {
|
||||
pn.notifmu.Lock()
|
||||
pn.notifs[f] = struct{}{}
|
||||
pn.notifmu.Unlock()
|
||||
}
|
||||
|
||||
// StopNotify unregisters Notifiee from receiving signals
|
||||
func (pn *peernet) StopNotify(f inet.Notifiee) {
|
||||
pn.notifmu.Lock()
|
||||
delete(pn.notifs, f)
|
||||
pn.notifmu.Unlock()
|
||||
}
|
||||
|
||||
// notifyAll runs the notification function on all Notifiees
|
||||
func (pn *peernet) notifyAll(notification func(f inet.Notifiee)) {
|
||||
pn.notifmu.Lock()
|
||||
var wg sync.WaitGroup
|
||||
for n := range pn.notifs {
|
||||
// make sure we dont block
|
||||
// and they dont block each other.
|
||||
wg.Add(1)
|
||||
go func(n inet.Notifiee) {
|
||||
defer wg.Done()
|
||||
notification(n)
|
||||
}(n)
|
||||
}
|
||||
wg.Wait()
|
||||
pn.notifmu.Unlock()
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
package mocknet
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
inet "github.com/libp2p/go-libp2p-net"
|
||||
peer "github.com/libp2p/go-libp2p-peer"
|
||||
)
|
||||
|
||||
// separate object so our interfaces are separate :)
|
||||
type printer struct {
|
||||
w io.Writer
|
||||
}
|
||||
|
||||
func (p *printer) MocknetLinks(mn Mocknet) {
|
||||
links := mn.Links()
|
||||
|
||||
fmt.Fprintf(p.w, "Mocknet link map:\n")
|
||||
for p1, lm := range links {
|
||||
fmt.Fprintf(p.w, "\t%s linked to:\n", peer.ID(p1))
|
||||
for p2, l := range lm {
|
||||
fmt.Fprintf(p.w, "\t\t%s (%d links)\n", peer.ID(p2), len(l))
|
||||
}
|
||||
}
|
||||
fmt.Fprintf(p.w, "\n")
|
||||
}
|
||||
|
||||
func (p *printer) NetworkConns(ni inet.Network) {
|
||||
|
||||
fmt.Fprintf(p.w, "%s connected to:\n", ni.LocalPeer())
|
||||
for _, c := range ni.Conns() {
|
||||
fmt.Fprintf(p.w, "\t%s (addr: %s)\n", c.RemotePeer(), c.RemoteMultiaddr())
|
||||
}
|
||||
fmt.Fprintf(p.w, "\n")
|
||||
}
|
||||
+266
@@ -0,0 +1,266 @@
|
||||
package mocknet
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
inet "github.com/libp2p/go-libp2p-net"
|
||||
protocol "github.com/libp2p/go-libp2p-protocol"
|
||||
)
|
||||
|
||||
// stream implements inet.Stream
|
||||
type stream struct {
|
||||
write *io.PipeWriter
|
||||
read *io.PipeReader
|
||||
conn *conn
|
||||
toDeliver chan *transportObject
|
||||
|
||||
reset chan struct{}
|
||||
close chan struct{}
|
||||
closed chan struct{}
|
||||
|
||||
writeErr error
|
||||
|
||||
protocol atomic.Value
|
||||
stat inet.Stat
|
||||
}
|
||||
|
||||
var ErrReset error = errors.New("stream reset")
|
||||
var ErrClosed error = errors.New("stream closed")
|
||||
|
||||
type transportObject struct {
|
||||
msg []byte
|
||||
arrivalTime time.Time
|
||||
}
|
||||
|
||||
func NewStream(w *io.PipeWriter, r *io.PipeReader, dir inet.Direction) *stream {
|
||||
s := &stream{
|
||||
read: r,
|
||||
write: w,
|
||||
reset: make(chan struct{}, 1),
|
||||
close: make(chan struct{}, 1),
|
||||
closed: make(chan struct{}),
|
||||
toDeliver: make(chan *transportObject),
|
||||
stat: inet.Stat{Direction: dir},
|
||||
}
|
||||
|
||||
go s.transport()
|
||||
return s
|
||||
}
|
||||
|
||||
// How to handle errors with writes?
|
||||
func (s *stream) Write(p []byte) (n int, err error) {
|
||||
l := s.conn.link
|
||||
delay := l.GetLatency() + l.RateLimit(len(p))
|
||||
t := time.Now().Add(delay)
|
||||
|
||||
// Copy it.
|
||||
cpy := make([]byte, len(p))
|
||||
copy(cpy, p)
|
||||
|
||||
select {
|
||||
case <-s.closed: // bail out if we're closing.
|
||||
return 0, s.writeErr
|
||||
case s.toDeliver <- &transportObject{msg: cpy, arrivalTime: t}:
|
||||
}
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func (s *stream) Protocol() protocol.ID {
|
||||
// Ignore type error. It means that the protocol is unset.
|
||||
p, _ := s.protocol.Load().(protocol.ID)
|
||||
return p
|
||||
}
|
||||
|
||||
func (s *stream) Stat() inet.Stat {
|
||||
return s.stat
|
||||
}
|
||||
|
||||
func (s *stream) SetProtocol(proto protocol.ID) {
|
||||
s.protocol.Store(proto)
|
||||
}
|
||||
|
||||
func (s *stream) Close() error {
|
||||
select {
|
||||
case s.close <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
<-s.closed
|
||||
if s.writeErr != ErrClosed {
|
||||
return s.writeErr
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *stream) Reset() error {
|
||||
// Cancel any pending reads/writes with an error.
|
||||
s.write.CloseWithError(ErrReset)
|
||||
s.read.CloseWithError(ErrReset)
|
||||
|
||||
select {
|
||||
case s.reset <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
<-s.closed
|
||||
|
||||
// No meaningful error case here.
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *stream) teardown() {
|
||||
// at this point, no streams are writing.
|
||||
s.conn.removeStream(s)
|
||||
|
||||
// Mark as closed.
|
||||
close(s.closed)
|
||||
|
||||
s.conn.net.notifyAll(func(n inet.Notifiee) {
|
||||
n.ClosedStream(s.conn.net, s)
|
||||
})
|
||||
}
|
||||
|
||||
func (s *stream) Conn() inet.Conn {
|
||||
return s.conn
|
||||
}
|
||||
|
||||
func (s *stream) SetDeadline(t time.Time) error {
|
||||
return &net.OpError{Op: "set", Net: "pipe", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
|
||||
}
|
||||
|
||||
func (s *stream) SetReadDeadline(t time.Time) error {
|
||||
return &net.OpError{Op: "set", Net: "pipe", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
|
||||
}
|
||||
|
||||
func (s *stream) SetWriteDeadline(t time.Time) error {
|
||||
return &net.OpError{Op: "set", Net: "pipe", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
|
||||
}
|
||||
|
||||
func (s *stream) Read(b []byte) (int, error) {
|
||||
return s.read.Read(b)
|
||||
}
|
||||
|
||||
// transport will grab message arrival times, wait until that time, and
|
||||
// then write the message out when it is scheduled to arrive
|
||||
func (s *stream) transport() {
|
||||
defer s.teardown()
|
||||
|
||||
bufsize := 256
|
||||
buf := new(bytes.Buffer)
|
||||
timer := time.NewTimer(0)
|
||||
if !timer.Stop() {
|
||||
select {
|
||||
case <-timer.C:
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
// cleanup
|
||||
defer timer.Stop()
|
||||
|
||||
// writeBuf writes the contents of buf through to the s.Writer.
|
||||
// done only when arrival time makes sense.
|
||||
drainBuf := func() error {
|
||||
if buf.Len() > 0 {
|
||||
_, err := s.write.Write(buf.Bytes())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
buf.Reset()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// deliverOrWait is a helper func that processes
|
||||
// an incoming packet. it waits until the arrival time,
|
||||
// and then writes things out.
|
||||
deliverOrWait := func(o *transportObject) error {
|
||||
buffered := len(o.msg) + buf.Len()
|
||||
|
||||
// Yes, we can end up extending a timer multiple times if we
|
||||
// keep on making small writes but that shouldn't be too much of an
|
||||
// issue. Fixing that would be painful.
|
||||
if !timer.Stop() {
|
||||
// FIXME: So, we *shouldn't* need to do this but we hang
|
||||
// here if we don't... Go bug?
|
||||
select {
|
||||
case <-timer.C:
|
||||
default:
|
||||
}
|
||||
}
|
||||
delay := o.arrivalTime.Sub(time.Now())
|
||||
if delay >= 0 {
|
||||
timer.Reset(delay)
|
||||
} else {
|
||||
timer.Reset(0)
|
||||
}
|
||||
|
||||
if buffered >= bufsize {
|
||||
select {
|
||||
case <-timer.C:
|
||||
case <-s.reset:
|
||||
select {
|
||||
case s.reset <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
return ErrReset
|
||||
}
|
||||
if err := drainBuf(); err != nil {
|
||||
return err
|
||||
}
|
||||
// write this message.
|
||||
_, err := s.write.Write(o.msg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
buf.Write(o.msg)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
for {
|
||||
// Reset takes precedent.
|
||||
select {
|
||||
case <-s.reset:
|
||||
s.writeErr = ErrReset
|
||||
return
|
||||
default:
|
||||
}
|
||||
|
||||
select {
|
||||
case <-s.reset:
|
||||
s.writeErr = ErrReset
|
||||
return
|
||||
case <-s.close:
|
||||
if err := drainBuf(); err != nil {
|
||||
s.resetWith(err)
|
||||
return
|
||||
}
|
||||
s.writeErr = s.write.Close()
|
||||
if s.writeErr == nil {
|
||||
s.writeErr = ErrClosed
|
||||
}
|
||||
return
|
||||
case o := <-s.toDeliver:
|
||||
if err := deliverOrWait(o); err != nil {
|
||||
s.resetWith(err)
|
||||
return
|
||||
}
|
||||
case <-timer.C: // ok, due to write it out.
|
||||
if err := drainBuf(); err != nil {
|
||||
s.resetWith(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *stream) resetWith(err error) {
|
||||
s.write.CloseWithError(err)
|
||||
s.read.CloseWithError(err)
|
||||
s.writeErr = err
|
||||
}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
package mocknet
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// A RateLimiter is used by a link to determine how long to wait before sending
|
||||
// data given a bandwidth cap.
|
||||
type RateLimiter struct {
|
||||
lock sync.Mutex
|
||||
bandwidth float64 // bytes per nanosecond
|
||||
allowance float64 // in bytes
|
||||
maxAllowance float64 // in bytes
|
||||
lastUpdate time.Time // when allowance was updated last
|
||||
count int // number of times rate limiting was applied
|
||||
duration time.Duration // total delay introduced due to rate limiting
|
||||
}
|
||||
|
||||
// Creates a new RateLimiter with bandwidth (in bytes/sec)
|
||||
func NewRateLimiter(bandwidth float64) *RateLimiter {
|
||||
// convert bandwidth to bytes per nanosecond
|
||||
b := bandwidth / float64(time.Second)
|
||||
return &RateLimiter{
|
||||
bandwidth: b,
|
||||
allowance: 0,
|
||||
maxAllowance: bandwidth,
|
||||
lastUpdate: time.Now(),
|
||||
}
|
||||
}
|
||||
|
||||
// Changes bandwidth of a RateLimiter and resets its allowance
|
||||
func (r *RateLimiter) UpdateBandwidth(bandwidth float64) {
|
||||
r.lock.Lock()
|
||||
defer r.lock.Unlock()
|
||||
// Convert bandwidth from bytes/second to bytes/nanosecond
|
||||
b := bandwidth / float64(time.Second)
|
||||
r.bandwidth = b
|
||||
// Reset allowance
|
||||
r.allowance = 0
|
||||
r.maxAllowance = bandwidth
|
||||
r.lastUpdate = time.Now()
|
||||
}
|
||||
|
||||
// Returns how long to wait before sending data with length 'dataSize' bytes
|
||||
func (r *RateLimiter) Limit(dataSize int) time.Duration {
|
||||
r.lock.Lock()
|
||||
defer r.lock.Unlock()
|
||||
// update time
|
||||
var duration time.Duration = time.Duration(0)
|
||||
if r.bandwidth == 0 {
|
||||
return duration
|
||||
}
|
||||
current := time.Now()
|
||||
elapsedTime := current.Sub(r.lastUpdate)
|
||||
r.lastUpdate = current
|
||||
|
||||
allowance := r.allowance + float64(elapsedTime)*r.bandwidth
|
||||
// allowance can't exceed bandwidth
|
||||
if allowance > r.maxAllowance {
|
||||
allowance = r.maxAllowance
|
||||
}
|
||||
|
||||
allowance -= float64(dataSize)
|
||||
if allowance < 0 {
|
||||
// sleep until allowance is back to 0
|
||||
duration = time.Duration(-allowance / r.bandwidth)
|
||||
// rate limiting was applied, record stats
|
||||
r.count++
|
||||
r.duration += duration
|
||||
}
|
||||
|
||||
r.allowance = allowance
|
||||
return duration
|
||||
}
|
||||
+515
@@ -0,0 +1,515 @@
|
||||
package identify
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
pb "github.com/libp2p/go-libp2p/p2p/protocol/identify/pb"
|
||||
|
||||
ggio "github.com/gogo/protobuf/io"
|
||||
logging "github.com/ipfs/go-log"
|
||||
ic "github.com/libp2p/go-libp2p-crypto"
|
||||
host "github.com/libp2p/go-libp2p-host"
|
||||
lgbl "github.com/libp2p/go-libp2p-loggables"
|
||||
inet "github.com/libp2p/go-libp2p-net"
|
||||
peer "github.com/libp2p/go-libp2p-peer"
|
||||
pstore "github.com/libp2p/go-libp2p-peerstore"
|
||||
ma "github.com/multiformats/go-multiaddr"
|
||||
msmux "github.com/multiformats/go-multistream"
|
||||
)
|
||||
|
||||
var log = logging.Logger("net/identify")
|
||||
|
||||
// ID is the protocol.ID of the Identify Service.
|
||||
const ID = "/ipfs/id/1.0.0"
|
||||
|
||||
// IDPush is the protocol.ID of the Identify push protocol
|
||||
const IDPush = "/ipfs/id/push/1.0.0"
|
||||
|
||||
// LibP2PVersion holds the current protocol version for a client running this code
|
||||
// TODO(jbenet): fix the versioning mess.
|
||||
const LibP2PVersion = "ipfs/0.1.0"
|
||||
|
||||
var ClientVersion = "go-libp2p/3.3.4"
|
||||
|
||||
// transientTTL is a short ttl for invalidated previously connected addrs
|
||||
const transientTTL = 10 * time.Second
|
||||
|
||||
// IDService is a structure that implements ProtocolIdentify.
|
||||
// It is a trivial service that gives the other peer some
|
||||
// useful information about the local peer. A sort of hello.
|
||||
//
|
||||
// The IDService sends:
|
||||
// * Our IPFS Protocol Version
|
||||
// * Our IPFS Agent Version
|
||||
// * Our public Listen Addresses
|
||||
type IDService struct {
|
||||
Host host.Host
|
||||
|
||||
ctx context.Context
|
||||
|
||||
// connections undergoing identification
|
||||
// for wait purposes
|
||||
currid map[inet.Conn]chan struct{}
|
||||
currmu sync.RWMutex
|
||||
|
||||
addrMu sync.Mutex
|
||||
|
||||
// our own observed addresses.
|
||||
// TODO: instead of expiring, remove these when we disconnect
|
||||
observedAddrs *ObservedAddrSet
|
||||
}
|
||||
|
||||
// NewIDService constructs a new *IDService and activates it by
|
||||
// attaching its stream handler to the given host.Host.
|
||||
func NewIDService(ctx context.Context, h host.Host) *IDService {
|
||||
s := &IDService{
|
||||
Host: h,
|
||||
ctx: ctx,
|
||||
currid: make(map[inet.Conn]chan struct{}),
|
||||
observedAddrs: NewObservedAddrSet(ctx),
|
||||
}
|
||||
h.SetStreamHandler(ID, s.requestHandler)
|
||||
h.SetStreamHandler(IDPush, s.pushHandler)
|
||||
h.Network().Notify((*netNotifiee)(s))
|
||||
return s
|
||||
}
|
||||
|
||||
// OwnObservedAddrs returns the addresses peers have reported we've dialed from
|
||||
func (ids *IDService) OwnObservedAddrs() []ma.Multiaddr {
|
||||
return ids.observedAddrs.Addrs()
|
||||
}
|
||||
|
||||
func (ids *IDService) ObservedAddrsFor(local ma.Multiaddr) []ma.Multiaddr {
|
||||
return ids.observedAddrs.AddrsFor(local)
|
||||
}
|
||||
|
||||
func (ids *IDService) IdentifyConn(c inet.Conn) {
|
||||
ids.currmu.Lock()
|
||||
if wait, found := ids.currid[c]; found {
|
||||
ids.currmu.Unlock()
|
||||
log.Debugf("IdentifyConn called twice on: %s", c)
|
||||
<-wait // already identifying it. wait for it.
|
||||
return
|
||||
}
|
||||
ch := make(chan struct{})
|
||||
ids.currid[c] = ch
|
||||
ids.currmu.Unlock()
|
||||
|
||||
defer func() {
|
||||
close(ch)
|
||||
ids.currmu.Lock()
|
||||
delete(ids.currid, c)
|
||||
ids.currmu.Unlock()
|
||||
}()
|
||||
|
||||
s, err := c.NewStream()
|
||||
if err != nil {
|
||||
log.Debugf("error opening initial stream for %s: %s", ID, err)
|
||||
log.Event(context.TODO(), "IdentifyOpenFailed", c.RemotePeer())
|
||||
c.Close()
|
||||
return
|
||||
}
|
||||
|
||||
s.SetProtocol(ID)
|
||||
|
||||
// ok give the response to our handler.
|
||||
if err := msmux.SelectProtoOrFail(ID, s); err != nil {
|
||||
log.Event(context.TODO(), "IdentifyOpenFailed", c.RemotePeer(), logging.Metadata{"error": err})
|
||||
s.Reset()
|
||||
return
|
||||
}
|
||||
|
||||
ids.responseHandler(s)
|
||||
}
|
||||
|
||||
func (ids *IDService) requestHandler(s inet.Stream) {
|
||||
defer inet.FullClose(s)
|
||||
c := s.Conn()
|
||||
|
||||
w := ggio.NewDelimitedWriter(s)
|
||||
mes := pb.Identify{}
|
||||
ids.populateMessage(&mes, s.Conn())
|
||||
w.WriteMsg(&mes)
|
||||
|
||||
log.Debugf("%s sent message to %s %s", ID,
|
||||
c.RemotePeer(), c.RemoteMultiaddr())
|
||||
}
|
||||
|
||||
func (ids *IDService) responseHandler(s inet.Stream) {
|
||||
c := s.Conn()
|
||||
|
||||
r := ggio.NewDelimitedReader(s, 2048)
|
||||
mes := pb.Identify{}
|
||||
if err := r.ReadMsg(&mes); err != nil {
|
||||
log.Warning("error reading identify message: ", err)
|
||||
s.Reset()
|
||||
return
|
||||
}
|
||||
ids.consumeMessage(&mes, c)
|
||||
log.Debugf("%s received message from %s %s", ID,
|
||||
c.RemotePeer(), c.RemoteMultiaddr())
|
||||
|
||||
go inet.FullClose(s)
|
||||
}
|
||||
|
||||
func (ids *IDService) pushHandler(s inet.Stream) {
|
||||
ids.responseHandler(s)
|
||||
}
|
||||
|
||||
func (ids *IDService) Push() {
|
||||
var wg sync.WaitGroup
|
||||
|
||||
ctx, cancel := context.WithTimeout(ids.ctx, 30*time.Second)
|
||||
ctx = inet.WithNoDial(ctx, "identify push")
|
||||
|
||||
for _, p := range ids.Host.Network().Peers() {
|
||||
wg.Add(1)
|
||||
go func(p peer.ID) {
|
||||
defer wg.Done()
|
||||
|
||||
s, err := ids.Host.NewStream(ctx, p, IDPush)
|
||||
if err != nil {
|
||||
log.Debugf("error opening push stream to %s: %s", p, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
rch := make(chan struct{}, 1)
|
||||
go func() {
|
||||
ids.requestHandler(s)
|
||||
rch <- struct{}{}
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-rch:
|
||||
case <-ctx.Done():
|
||||
// this is taking too long, abort!
|
||||
s.Reset()
|
||||
}
|
||||
}(p)
|
||||
}
|
||||
|
||||
// this supervisory goroutine is necessary to cancel the context
|
||||
go func() {
|
||||
wg.Wait()
|
||||
cancel()
|
||||
}()
|
||||
}
|
||||
|
||||
func (ids *IDService) populateMessage(mes *pb.Identify, c inet.Conn) {
|
||||
|
||||
// set protocols this node is currently handling
|
||||
protos := ids.Host.Mux().Protocols()
|
||||
mes.Protocols = make([]string, len(protos))
|
||||
for i, p := range protos {
|
||||
mes.Protocols[i] = string(p)
|
||||
}
|
||||
|
||||
// observed address so other side is informed of their
|
||||
// "public" address, at least in relation to us.
|
||||
mes.ObservedAddr = c.RemoteMultiaddr().Bytes()
|
||||
|
||||
// set listen addrs, get our latest addrs from Host.
|
||||
laddrs := ids.Host.Addrs()
|
||||
mes.ListenAddrs = make([][]byte, len(laddrs))
|
||||
for i, addr := range laddrs {
|
||||
mes.ListenAddrs[i] = addr.Bytes()
|
||||
}
|
||||
log.Debugf("%s sent listen addrs to %s: %s", c.LocalPeer(), c.RemotePeer(), laddrs)
|
||||
|
||||
// set our public key
|
||||
ownKey := ids.Host.Peerstore().PubKey(ids.Host.ID())
|
||||
|
||||
// check if we even have a public key.
|
||||
if ownKey == nil {
|
||||
// public key is nil. We are either using insecure transport or something erratic happened.
|
||||
// check if we're even operating in "secure mode"
|
||||
if ids.Host.Peerstore().PrivKey(ids.Host.ID()) != nil {
|
||||
// private key is present. But NO public key. Something bad happened.
|
||||
log.Errorf("did not have own public key in Peerstore")
|
||||
}
|
||||
// if neither of the key is present it is safe to assume that we are using an insecure transport.
|
||||
} else {
|
||||
// public key is present. Safe to proceed.
|
||||
if kb, err := ownKey.Bytes(); err != nil {
|
||||
log.Errorf("failed to convert key to bytes")
|
||||
} else {
|
||||
mes.PublicKey = kb
|
||||
}
|
||||
}
|
||||
|
||||
// set protocol versions
|
||||
pv := LibP2PVersion
|
||||
av := ClientVersion
|
||||
mes.ProtocolVersion = &pv
|
||||
mes.AgentVersion = &av
|
||||
}
|
||||
|
||||
func (ids *IDService) consumeMessage(mes *pb.Identify, c inet.Conn) {
|
||||
p := c.RemotePeer()
|
||||
|
||||
// mes.Protocols
|
||||
ids.Host.Peerstore().SetProtocols(p, mes.Protocols...)
|
||||
|
||||
// mes.ObservedAddr
|
||||
ids.consumeObservedAddress(mes.GetObservedAddr(), c)
|
||||
|
||||
// mes.ListenAddrs
|
||||
laddrs := mes.GetListenAddrs()
|
||||
lmaddrs := make([]ma.Multiaddr, 0, len(laddrs))
|
||||
for _, addr := range laddrs {
|
||||
maddr, err := ma.NewMultiaddrBytes(addr)
|
||||
if err != nil {
|
||||
log.Debugf("%s failed to parse multiaddr from %s %s", ID,
|
||||
p, c.RemoteMultiaddr())
|
||||
continue
|
||||
}
|
||||
lmaddrs = append(lmaddrs, maddr)
|
||||
}
|
||||
|
||||
// NOTE: Do not add `c.RemoteMultiaddr()` to the peerstore if the remote
|
||||
// peer doesn't tell us to do so. Otherwise, we'll advertise it.
|
||||
//
|
||||
// This can cause an "addr-splosion" issue where the network will slowly
|
||||
// gossip and collect observed but unadvertised addresses. Given a NAT
|
||||
// that picks random source ports, this can cause DHT nodes to collect
|
||||
// many undialable addresses for other peers.
|
||||
|
||||
// Extend the TTLs on the known (probably) good addresses.
|
||||
// Taking the lock ensures that we don't concurrently process a disconnect.
|
||||
ids.addrMu.Lock()
|
||||
switch ids.Host.Network().Connectedness(p) {
|
||||
case inet.Connected:
|
||||
// invalidate previous addrs -- we use a transient ttl instead of 0 to ensure there
|
||||
// is no period of having no good addrs whatsoever
|
||||
ids.Host.Peerstore().UpdateAddrs(p, pstore.ConnectedAddrTTL, transientTTL)
|
||||
ids.Host.Peerstore().AddAddrs(p, lmaddrs, pstore.ConnectedAddrTTL)
|
||||
default:
|
||||
ids.Host.Peerstore().UpdateAddrs(p, pstore.ConnectedAddrTTL, transientTTL)
|
||||
ids.Host.Peerstore().AddAddrs(p, lmaddrs, pstore.RecentlyConnectedAddrTTL)
|
||||
}
|
||||
ids.addrMu.Unlock()
|
||||
|
||||
log.Debugf("%s received listen addrs for %s: %s", c.LocalPeer(), c.RemotePeer(), lmaddrs)
|
||||
|
||||
// get protocol versions
|
||||
pv := mes.GetProtocolVersion()
|
||||
av := mes.GetAgentVersion()
|
||||
|
||||
ids.Host.Peerstore().Put(p, "ProtocolVersion", pv)
|
||||
ids.Host.Peerstore().Put(p, "AgentVersion", av)
|
||||
|
||||
// get the key from the other side. we may not have it (no-auth transport)
|
||||
ids.consumeReceivedPubKey(c, mes.PublicKey)
|
||||
}
|
||||
|
||||
func (ids *IDService) consumeReceivedPubKey(c inet.Conn, kb []byte) {
|
||||
lp := c.LocalPeer()
|
||||
rp := c.RemotePeer()
|
||||
|
||||
if kb == nil {
|
||||
log.Debugf("%s did not receive public key for remote peer: %s", lp, rp)
|
||||
return
|
||||
}
|
||||
|
||||
newKey, err := ic.UnmarshalPublicKey(kb)
|
||||
if err != nil {
|
||||
log.Warningf("%s cannot unmarshal key from remote peer: %s, %s", lp, rp, err)
|
||||
return
|
||||
}
|
||||
|
||||
// verify key matches peer.ID
|
||||
np, err := peer.IDFromPublicKey(newKey)
|
||||
if err != nil {
|
||||
log.Debugf("%s cannot get peer.ID from key of remote peer: %s, %s", lp, rp, err)
|
||||
return
|
||||
}
|
||||
|
||||
if np != rp {
|
||||
// if the newKey's peer.ID does not match known peer.ID...
|
||||
|
||||
if rp == "" && np != "" {
|
||||
// if local peerid is empty, then use the new, sent key.
|
||||
err := ids.Host.Peerstore().AddPubKey(rp, newKey)
|
||||
if err != nil {
|
||||
log.Debugf("%s could not add key for %s to peerstore: %s", lp, rp, err)
|
||||
}
|
||||
|
||||
} else {
|
||||
// we have a local peer.ID and it does not match the sent key... error.
|
||||
log.Errorf("%s received key for remote peer %s mismatch: %s", lp, rp, np)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
currKey := ids.Host.Peerstore().PubKey(rp)
|
||||
if currKey == nil {
|
||||
// no key? no auth transport. set this one.
|
||||
err := ids.Host.Peerstore().AddPubKey(rp, newKey)
|
||||
if err != nil {
|
||||
log.Debugf("%s could not add key for %s to peerstore: %s", lp, rp, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ok, we have a local key, we should verify they match.
|
||||
if currKey.Equals(newKey) {
|
||||
return // ok great. we're done.
|
||||
}
|
||||
|
||||
// weird, got a different key... but the different key MATCHES the peer.ID.
|
||||
// this odd. let's log error and investigate. this should basically never happen
|
||||
// and it means we have something funky going on and possibly a bug.
|
||||
log.Errorf("%s identify got a different key for: %s", lp, rp)
|
||||
|
||||
// okay... does ours NOT match the remote peer.ID?
|
||||
cp, err := peer.IDFromPublicKey(currKey)
|
||||
if err != nil {
|
||||
log.Errorf("%s cannot get peer.ID from local key of remote peer: %s, %s", lp, rp, err)
|
||||
return
|
||||
}
|
||||
if cp != rp {
|
||||
log.Errorf("%s local key for remote peer %s yields different peer.ID: %s", lp, rp, cp)
|
||||
return
|
||||
}
|
||||
|
||||
// okay... curr key DOES NOT match new key. both match peer.ID. wat?
|
||||
log.Errorf("%s local key and received key for %s do not match, but match peer.ID", lp, rp)
|
||||
}
|
||||
|
||||
// HasConsistentTransport returns true if the address 'a' shares a
|
||||
// protocol set with any address in the green set. This is used
|
||||
// to check if a given address might be one of the addresses a peer is
|
||||
// listening on.
|
||||
func HasConsistentTransport(a ma.Multiaddr, green []ma.Multiaddr) bool {
|
||||
protosMatch := func(a, b []ma.Protocol) bool {
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
|
||||
for i, p := range a {
|
||||
if b[i].Code != p.Code {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
protos := a.Protocols()
|
||||
|
||||
for _, ga := range green {
|
||||
if protosMatch(protos, ga.Protocols()) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// IdentifyWait returns a channel which will be closed once
|
||||
// "ProtocolIdentify" (handshake3) finishes on given conn.
|
||||
// This happens async so the connection can start to be used
|
||||
// even if handshake3 knowledge is not necesary.
|
||||
// Users **MUST** call IdentifyWait _after_ IdentifyConn
|
||||
func (ids *IDService) IdentifyWait(c inet.Conn) <-chan struct{} {
|
||||
ids.currmu.Lock()
|
||||
ch, found := ids.currid[c]
|
||||
ids.currmu.Unlock()
|
||||
if found {
|
||||
return ch
|
||||
}
|
||||
|
||||
// if not found, it means we are already done identifying it, or
|
||||
// haven't even started. either way, return a new channel closed.
|
||||
ch = make(chan struct{})
|
||||
close(ch)
|
||||
return ch
|
||||
}
|
||||
|
||||
func (ids *IDService) consumeObservedAddress(observed []byte, c inet.Conn) {
|
||||
if observed == nil {
|
||||
return
|
||||
}
|
||||
|
||||
maddr, err := ma.NewMultiaddrBytes(observed)
|
||||
if err != nil {
|
||||
log.Debugf("error parsing received observed addr for %s: %s", c, err)
|
||||
return
|
||||
}
|
||||
|
||||
// we should only use ObservedAddr when our connection's LocalAddr is one
|
||||
// of our ListenAddrs. If we Dial out using an ephemeral addr, knowing that
|
||||
// address's external mapping is not very useful because the port will not be
|
||||
// the same as the listen addr.
|
||||
ifaceaddrs, err := ids.Host.Network().InterfaceListenAddresses()
|
||||
if err != nil {
|
||||
log.Infof("failed to get interface listen addrs", err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Debugf("identify identifying observed multiaddr: %s %s", c.LocalMultiaddr(), ifaceaddrs)
|
||||
if !addrInAddrs(c.LocalMultiaddr(), ifaceaddrs) && !addrInAddrs(c.LocalMultiaddr(), ids.Host.Network().ListenAddresses()) {
|
||||
// not in our list
|
||||
return
|
||||
}
|
||||
|
||||
if !HasConsistentTransport(maddr, ids.Host.Addrs()) {
|
||||
log.Debugf("ignoring observed multiaddr that doesn't match the transports of any addresses we're announcing", c.RemoteMultiaddr())
|
||||
return
|
||||
}
|
||||
|
||||
// ok! we have the observed version of one of our ListenAddresses!
|
||||
log.Debugf("added own observed listen addr: %s --> %s", c.LocalMultiaddr(), maddr)
|
||||
ids.observedAddrs.Add(maddr, c.LocalMultiaddr(), c.RemoteMultiaddr(),
|
||||
c.Stat().Direction)
|
||||
}
|
||||
|
||||
func addrInAddrs(a ma.Multiaddr, as []ma.Multiaddr) bool {
|
||||
for _, b := range as {
|
||||
if a.Equal(b) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// netNotifiee defines methods to be used with the IpfsDHT
|
||||
type netNotifiee IDService
|
||||
|
||||
func (nn *netNotifiee) IDService() *IDService {
|
||||
return (*IDService)(nn)
|
||||
}
|
||||
|
||||
func (nn *netNotifiee) Connected(n inet.Network, v inet.Conn) {
|
||||
// TODO: deprecate the setConnHandler hook, and kick off
|
||||
// identification here.
|
||||
}
|
||||
|
||||
func (nn *netNotifiee) Disconnected(n inet.Network, v inet.Conn) {
|
||||
// undo the setting of addresses to peer.ConnectedAddrTTL we did
|
||||
ids := nn.IDService()
|
||||
ids.addrMu.Lock()
|
||||
defer ids.addrMu.Unlock()
|
||||
|
||||
if ids.Host.Network().Connectedness(v.RemotePeer()) != inet.Connected {
|
||||
// Last disconnect.
|
||||
ps := ids.Host.Peerstore()
|
||||
ps.UpdateAddrs(v.RemotePeer(), pstore.ConnectedAddrTTL, pstore.RecentlyConnectedAddrTTL)
|
||||
}
|
||||
}
|
||||
|
||||
func (nn *netNotifiee) OpenedStream(n inet.Network, v inet.Stream) {}
|
||||
func (nn *netNotifiee) ClosedStream(n inet.Network, v inet.Stream) {}
|
||||
func (nn *netNotifiee) Listen(n inet.Network, a ma.Multiaddr) {}
|
||||
func (nn *netNotifiee) ListenClose(n inet.Network, a ma.Multiaddr) {}
|
||||
|
||||
func logProtocolMismatchDisconnect(c inet.Conn, protocol, agent string) {
|
||||
lm := make(lgbl.DeferredMap)
|
||||
lm["remotePeer"] = func() interface{} { return c.RemotePeer().Pretty() }
|
||||
lm["remoteAddr"] = func() interface{} { return c.RemoteMultiaddr().String() }
|
||||
lm["protocolVersion"] = protocol
|
||||
lm["agentVersion"] = agent
|
||||
log.Event(context.TODO(), "IdentifyProtocolMismatch", lm)
|
||||
log.Debugf("IdentifyProtocolMismatch %s %s %s (disconnected)", c.RemotePeer(), protocol, agent)
|
||||
}
|
||||
+229
@@ -0,0 +1,229 @@
|
||||
package identify
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
net "github.com/libp2p/go-libp2p-net"
|
||||
pstore "github.com/libp2p/go-libp2p-peerstore"
|
||||
ma "github.com/multiformats/go-multiaddr"
|
||||
)
|
||||
|
||||
const ActivationThresh = 4
|
||||
|
||||
var GCInterval = 10 * time.Minute
|
||||
|
||||
type observation struct {
|
||||
seenTime time.Time
|
||||
connDirection net.Direction
|
||||
}
|
||||
|
||||
// ObservedAddr is an entry for an address reported by our peers.
|
||||
// We only use addresses that:
|
||||
// - have been observed at least 4 times in last 1h. (counter symmetric nats)
|
||||
// - have been observed at least once recently (1h), because our position in the
|
||||
// network, or network port mapppings, may have changed.
|
||||
type ObservedAddr struct {
|
||||
Addr ma.Multiaddr
|
||||
SeenBy map[string]observation // peer(observer) address -> observation info
|
||||
LastSeen time.Time
|
||||
}
|
||||
|
||||
func (oa *ObservedAddr) activated(ttl time.Duration) bool {
|
||||
// We only activate if in the TTL other peers observed the same address
|
||||
// of ours at least 4 times.
|
||||
return len(oa.SeenBy) >= ActivationThresh
|
||||
}
|
||||
|
||||
type newObservation struct {
|
||||
observed, local, observer ma.Multiaddr
|
||||
direction net.Direction
|
||||
}
|
||||
|
||||
// ObservedAddrSet keeps track of a set of ObservedAddrs
|
||||
// the zero-value is ready to be used.
|
||||
type ObservedAddrSet struct {
|
||||
sync.RWMutex // guards whole datastruct.
|
||||
|
||||
// local(internal) address -> list of observed(external) addresses
|
||||
addrs map[string][]*ObservedAddr
|
||||
ttl time.Duration
|
||||
|
||||
// this is the worker channel
|
||||
wch chan newObservation
|
||||
}
|
||||
|
||||
func NewObservedAddrSet(ctx context.Context) *ObservedAddrSet {
|
||||
oas := &ObservedAddrSet{
|
||||
addrs: make(map[string][]*ObservedAddr),
|
||||
ttl: pstore.OwnObservedAddrTTL,
|
||||
wch: make(chan newObservation, 16),
|
||||
}
|
||||
go oas.worker(ctx)
|
||||
return oas
|
||||
}
|
||||
|
||||
// AddrsFor return all activated observed addresses associated with the given
|
||||
// (resolved) listen address.
|
||||
func (oas *ObservedAddrSet) AddrsFor(addr ma.Multiaddr) (addrs []ma.Multiaddr) {
|
||||
oas.RLock()
|
||||
defer oas.RUnlock()
|
||||
|
||||
if len(oas.addrs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
key := string(addr.Bytes())
|
||||
observedAddrs, ok := oas.addrs[key]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
for _, a := range observedAddrs {
|
||||
if now.Sub(a.LastSeen) <= oas.ttl && a.activated(oas.ttl) {
|
||||
addrs = append(addrs, a.Addr)
|
||||
}
|
||||
}
|
||||
|
||||
return addrs
|
||||
}
|
||||
|
||||
// Addrs return all activated observed addresses
|
||||
func (oas *ObservedAddrSet) Addrs() (addrs []ma.Multiaddr) {
|
||||
oas.RLock()
|
||||
defer oas.RUnlock()
|
||||
|
||||
if len(oas.addrs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
for _, observedAddrs := range oas.addrs {
|
||||
for _, a := range observedAddrs {
|
||||
if now.Sub(a.LastSeen) <= oas.ttl && a.activated(oas.ttl) {
|
||||
addrs = append(addrs, a.Addr)
|
||||
}
|
||||
}
|
||||
}
|
||||
return addrs
|
||||
}
|
||||
|
||||
func (oas *ObservedAddrSet) Add(observed, local, observer ma.Multiaddr,
|
||||
direction net.Direction) {
|
||||
select {
|
||||
case oas.wch <- newObservation{observed: observed, local: local, observer: observer, direction: direction}:
|
||||
default:
|
||||
log.Debugf("dropping address observation of %s; buffer full", observed)
|
||||
}
|
||||
}
|
||||
|
||||
func (oas *ObservedAddrSet) worker(ctx context.Context) {
|
||||
ticker := time.NewTicker(GCInterval)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case obs := <-oas.wch:
|
||||
oas.doAdd(obs.observed, obs.local, obs.observer, obs.direction)
|
||||
|
||||
case <-ticker.C:
|
||||
oas.gc()
|
||||
|
||||
case <-ctx.Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (oas *ObservedAddrSet) gc() {
|
||||
oas.Lock()
|
||||
defer oas.Unlock()
|
||||
|
||||
now := time.Now()
|
||||
for local, observedAddrs := range oas.addrs {
|
||||
// TODO we can do this without allocating by compacting the array in place
|
||||
filteredAddrs := make([]*ObservedAddr, 0, len(observedAddrs))
|
||||
|
||||
for _, a := range observedAddrs {
|
||||
// clean up SeenBy set
|
||||
for k, ob := range a.SeenBy {
|
||||
if now.Sub(ob.seenTime) > oas.ttl*ActivationThresh {
|
||||
delete(a.SeenBy, k)
|
||||
}
|
||||
}
|
||||
|
||||
// leave only alive observed addresses
|
||||
if now.Sub(a.LastSeen) <= oas.ttl {
|
||||
filteredAddrs = append(filteredAddrs, a)
|
||||
}
|
||||
}
|
||||
if len(filteredAddrs) > 0 {
|
||||
oas.addrs[local] = filteredAddrs
|
||||
} else {
|
||||
delete(oas.addrs, local)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (oas *ObservedAddrSet) doAdd(observed, local, observer ma.Multiaddr,
|
||||
direction net.Direction) {
|
||||
|
||||
now := time.Now()
|
||||
observerString := observerGroup(observer)
|
||||
localString := string(local.Bytes())
|
||||
ob := observation{
|
||||
seenTime: now,
|
||||
connDirection: direction,
|
||||
}
|
||||
|
||||
oas.Lock()
|
||||
defer oas.Unlock()
|
||||
|
||||
observedAddrs := oas.addrs[localString]
|
||||
// check if observed address seen yet, if so, update it
|
||||
for i, previousObserved := range observedAddrs {
|
||||
if previousObserved.Addr.Equal(observed) {
|
||||
observedAddrs[i].SeenBy[observerString] = ob
|
||||
observedAddrs[i].LastSeen = now
|
||||
return
|
||||
}
|
||||
}
|
||||
// observed address not seen yet, append it
|
||||
oas.addrs[localString] = append(oas.addrs[localString], &ObservedAddr{
|
||||
Addr: observed,
|
||||
SeenBy: map[string]observation{
|
||||
observerString: ob,
|
||||
},
|
||||
LastSeen: now,
|
||||
})
|
||||
}
|
||||
|
||||
// observerGroup is a function that determines what part of
|
||||
// a multiaddr counts as a different observer. for example,
|
||||
// two ipfs nodes at the same IP/TCP transport would get
|
||||
// the exact same NAT mapping; they would count as the
|
||||
// same observer. This may protect against NATs who assign
|
||||
// different ports to addresses at different IP hosts, but
|
||||
// not TCP ports.
|
||||
//
|
||||
// Here, we use the root multiaddr address. This is mostly
|
||||
// IP addresses. In practice, this is what we want.
|
||||
func observerGroup(m ma.Multiaddr) string {
|
||||
//TODO: If IPv6 rolls out we should mark /64 routing zones as one group
|
||||
first, _ := ma.SplitFirst(m)
|
||||
return string(first.Bytes())
|
||||
}
|
||||
|
||||
func (oas *ObservedAddrSet) SetTTL(ttl time.Duration) {
|
||||
oas.Lock()
|
||||
defer oas.Unlock()
|
||||
oas.ttl = ttl
|
||||
}
|
||||
|
||||
func (oas *ObservedAddrSet) TTL() time.Duration {
|
||||
oas.RLock()
|
||||
defer oas.RUnlock()
|
||||
return oas.ttl
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
PB = $(wildcard *.proto)
|
||||
GO = $(PB:.proto=.pb.go)
|
||||
|
||||
all: $(GO)
|
||||
|
||||
%.pb.go: %.proto
|
||||
protoc --proto_path=$(GOPATH)/src:. --gogofast_out=. $<
|
||||
|
||||
clean:
|
||||
rm -f *.pb.go
|
||||
rm -f *.go
|
||||
+635
@@ -0,0 +1,635 @@
|
||||
// Code generated by protoc-gen-gogo. DO NOT EDIT.
|
||||
// source: identify.proto
|
||||
|
||||
package identify_pb
|
||||
|
||||
import (
|
||||
fmt "fmt"
|
||||
proto "github.com/gogo/protobuf/proto"
|
||||
io "io"
|
||||
math "math"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package
|
||||
|
||||
type Identify struct {
|
||||
// protocolVersion determines compatibility between peers
|
||||
ProtocolVersion *string `protobuf:"bytes,5,opt,name=protocolVersion" json:"protocolVersion,omitempty"`
|
||||
// agentVersion is like a UserAgent string in browsers, or client version in bittorrent
|
||||
// includes the client name and client.
|
||||
AgentVersion *string `protobuf:"bytes,6,opt,name=agentVersion" json:"agentVersion,omitempty"`
|
||||
// publicKey is this node's public key (which also gives its node.ID)
|
||||
// - may not need to be sent, as secure channel implies it has been sent.
|
||||
// - then again, if we change / disable secure channel, may still want it.
|
||||
PublicKey []byte `protobuf:"bytes,1,opt,name=publicKey" json:"publicKey,omitempty"`
|
||||
// listenAddrs are the multiaddrs the sender node listens for open connections on
|
||||
ListenAddrs [][]byte `protobuf:"bytes,2,rep,name=listenAddrs" json:"listenAddrs,omitempty"`
|
||||
// oservedAddr is the multiaddr of the remote endpoint that the sender node perceives
|
||||
// this is useful information to convey to the other side, as it helps the remote endpoint
|
||||
// determine whether its connection to the local peer goes through NAT.
|
||||
ObservedAddr []byte `protobuf:"bytes,4,opt,name=observedAddr" json:"observedAddr,omitempty"`
|
||||
// protocols are the services this node is running
|
||||
Protocols []string `protobuf:"bytes,3,rep,name=protocols" json:"protocols,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *Identify) Reset() { *m = Identify{} }
|
||||
func (m *Identify) String() string { return proto.CompactTextString(m) }
|
||||
func (*Identify) ProtoMessage() {}
|
||||
func (*Identify) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_83f1e7e6b485409f, []int{0}
|
||||
}
|
||||
func (m *Identify) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
}
|
||||
func (m *Identify) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
if deterministic {
|
||||
return xxx_messageInfo_Identify.Marshal(b, m, deterministic)
|
||||
} else {
|
||||
b = b[:cap(b)]
|
||||
n, err := m.MarshalTo(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return b[:n], nil
|
||||
}
|
||||
}
|
||||
func (m *Identify) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_Identify.Merge(m, src)
|
||||
}
|
||||
func (m *Identify) XXX_Size() int {
|
||||
return m.Size()
|
||||
}
|
||||
func (m *Identify) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_Identify.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_Identify proto.InternalMessageInfo
|
||||
|
||||
func (m *Identify) GetProtocolVersion() string {
|
||||
if m != nil && m.ProtocolVersion != nil {
|
||||
return *m.ProtocolVersion
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Identify) GetAgentVersion() string {
|
||||
if m != nil && m.AgentVersion != nil {
|
||||
return *m.AgentVersion
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Identify) GetPublicKey() []byte {
|
||||
if m != nil {
|
||||
return m.PublicKey
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Identify) GetListenAddrs() [][]byte {
|
||||
if m != nil {
|
||||
return m.ListenAddrs
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Identify) GetObservedAddr() []byte {
|
||||
if m != nil {
|
||||
return m.ObservedAddr
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Identify) GetProtocols() []string {
|
||||
if m != nil {
|
||||
return m.Protocols
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*Identify)(nil), "identify.pb.Identify")
|
||||
}
|
||||
|
||||
func init() { proto.RegisterFile("identify.proto", fileDescriptor_83f1e7e6b485409f) }
|
||||
|
||||
var fileDescriptor_83f1e7e6b485409f = []byte{
|
||||
// 187 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0xcb, 0x4c, 0x49, 0xcd,
|
||||
0x2b, 0xc9, 0x4c, 0xab, 0xd4, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x46, 0xf0, 0x93, 0x94,
|
||||
0x6e, 0x31, 0x72, 0x71, 0x78, 0x42, 0xf9, 0x42, 0x1a, 0x5c, 0xfc, 0x60, 0x25, 0xc9, 0xf9, 0x39,
|
||||
0x61, 0xa9, 0x45, 0xc5, 0x99, 0xf9, 0x79, 0x12, 0xac, 0x0a, 0x8c, 0x1a, 0x9c, 0x41, 0xe8, 0xc2,
|
||||
0x42, 0x4a, 0x5c, 0x3c, 0x89, 0xe9, 0xa9, 0x79, 0x25, 0x30, 0x65, 0x6c, 0x60, 0x65, 0x28, 0x62,
|
||||
0x42, 0x32, 0x5c, 0x9c, 0x05, 0xa5, 0x49, 0x39, 0x99, 0xc9, 0xde, 0xa9, 0x95, 0x12, 0x8c, 0x0a,
|
||||
0x8c, 0x1a, 0x3c, 0x41, 0x08, 0x01, 0x21, 0x05, 0x2e, 0xee, 0x9c, 0xcc, 0xe2, 0x92, 0xd4, 0x3c,
|
||||
0xc7, 0x94, 0x94, 0xa2, 0x62, 0x09, 0x26, 0x05, 0x66, 0x0d, 0x9e, 0x20, 0x64, 0x21, 0x90, 0x1d,
|
||||
0xf9, 0x49, 0xc5, 0xa9, 0x45, 0x65, 0xa9, 0x29, 0x20, 0x01, 0x09, 0x16, 0xb0, 0x11, 0x28, 0x62,
|
||||
0x60, 0x3b, 0xa0, 0x4e, 0x2b, 0x96, 0x60, 0x56, 0x60, 0xd6, 0xe0, 0x0c, 0x42, 0x08, 0x38, 0xf1,
|
||||
0x9c, 0x78, 0x24, 0xc7, 0x78, 0xe1, 0x91, 0x1c, 0xe3, 0x83, 0x47, 0x72, 0x8c, 0x80, 0x00, 0x00,
|
||||
0x00, 0xff, 0xff, 0xc2, 0x2c, 0x19, 0x46, 0x08, 0x01, 0x00, 0x00,
|
||||
}
|
||||
|
||||
func (m *Identify) Marshal() (dAtA []byte, err error) {
|
||||
size := m.Size()
|
||||
dAtA = make([]byte, size)
|
||||
n, err := m.MarshalTo(dAtA)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return dAtA[:n], nil
|
||||
}
|
||||
|
||||
func (m *Identify) MarshalTo(dAtA []byte) (int, error) {
|
||||
var i int
|
||||
_ = i
|
||||
var l int
|
||||
_ = l
|
||||
if m.PublicKey != nil {
|
||||
dAtA[i] = 0xa
|
||||
i++
|
||||
i = encodeVarintIdentify(dAtA, i, uint64(len(m.PublicKey)))
|
||||
i += copy(dAtA[i:], m.PublicKey)
|
||||
}
|
||||
if len(m.ListenAddrs) > 0 {
|
||||
for _, b := range m.ListenAddrs {
|
||||
dAtA[i] = 0x12
|
||||
i++
|
||||
i = encodeVarintIdentify(dAtA, i, uint64(len(b)))
|
||||
i += copy(dAtA[i:], b)
|
||||
}
|
||||
}
|
||||
if len(m.Protocols) > 0 {
|
||||
for _, s := range m.Protocols {
|
||||
dAtA[i] = 0x1a
|
||||
i++
|
||||
l = len(s)
|
||||
for l >= 1<<7 {
|
||||
dAtA[i] = uint8(uint64(l)&0x7f | 0x80)
|
||||
l >>= 7
|
||||
i++
|
||||
}
|
||||
dAtA[i] = uint8(l)
|
||||
i++
|
||||
i += copy(dAtA[i:], s)
|
||||
}
|
||||
}
|
||||
if m.ObservedAddr != nil {
|
||||
dAtA[i] = 0x22
|
||||
i++
|
||||
i = encodeVarintIdentify(dAtA, i, uint64(len(m.ObservedAddr)))
|
||||
i += copy(dAtA[i:], m.ObservedAddr)
|
||||
}
|
||||
if m.ProtocolVersion != nil {
|
||||
dAtA[i] = 0x2a
|
||||
i++
|
||||
i = encodeVarintIdentify(dAtA, i, uint64(len(*m.ProtocolVersion)))
|
||||
i += copy(dAtA[i:], *m.ProtocolVersion)
|
||||
}
|
||||
if m.AgentVersion != nil {
|
||||
dAtA[i] = 0x32
|
||||
i++
|
||||
i = encodeVarintIdentify(dAtA, i, uint64(len(*m.AgentVersion)))
|
||||
i += copy(dAtA[i:], *m.AgentVersion)
|
||||
}
|
||||
if m.XXX_unrecognized != nil {
|
||||
i += copy(dAtA[i:], m.XXX_unrecognized)
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
|
||||
func encodeVarintIdentify(dAtA []byte, offset int, v uint64) int {
|
||||
for v >= 1<<7 {
|
||||
dAtA[offset] = uint8(v&0x7f | 0x80)
|
||||
v >>= 7
|
||||
offset++
|
||||
}
|
||||
dAtA[offset] = uint8(v)
|
||||
return offset + 1
|
||||
}
|
||||
func (m *Identify) Size() (n int) {
|
||||
if m == nil {
|
||||
return 0
|
||||
}
|
||||
var l int
|
||||
_ = l
|
||||
if m.PublicKey != nil {
|
||||
l = len(m.PublicKey)
|
||||
n += 1 + l + sovIdentify(uint64(l))
|
||||
}
|
||||
if len(m.ListenAddrs) > 0 {
|
||||
for _, b := range m.ListenAddrs {
|
||||
l = len(b)
|
||||
n += 1 + l + sovIdentify(uint64(l))
|
||||
}
|
||||
}
|
||||
if len(m.Protocols) > 0 {
|
||||
for _, s := range m.Protocols {
|
||||
l = len(s)
|
||||
n += 1 + l + sovIdentify(uint64(l))
|
||||
}
|
||||
}
|
||||
if m.ObservedAddr != nil {
|
||||
l = len(m.ObservedAddr)
|
||||
n += 1 + l + sovIdentify(uint64(l))
|
||||
}
|
||||
if m.ProtocolVersion != nil {
|
||||
l = len(*m.ProtocolVersion)
|
||||
n += 1 + l + sovIdentify(uint64(l))
|
||||
}
|
||||
if m.AgentVersion != nil {
|
||||
l = len(*m.AgentVersion)
|
||||
n += 1 + l + sovIdentify(uint64(l))
|
||||
}
|
||||
if m.XXX_unrecognized != nil {
|
||||
n += len(m.XXX_unrecognized)
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func sovIdentify(x uint64) (n int) {
|
||||
for {
|
||||
n++
|
||||
x >>= 7
|
||||
if x == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
func sozIdentify(x uint64) (n int) {
|
||||
return sovIdentify(uint64((x << 1) ^ uint64((int64(x) >> 63))))
|
||||
}
|
||||
func (m *Identify) Unmarshal(dAtA []byte) error {
|
||||
l := len(dAtA)
|
||||
iNdEx := 0
|
||||
for iNdEx < l {
|
||||
preIndex := iNdEx
|
||||
var wire uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowIdentify
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
wire |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
fieldNum := int32(wire >> 3)
|
||||
wireType := int(wire & 0x7)
|
||||
if wireType == 4 {
|
||||
return fmt.Errorf("proto: Identify: wiretype end group for non-group")
|
||||
}
|
||||
if fieldNum <= 0 {
|
||||
return fmt.Errorf("proto: Identify: illegal tag %d (wire type %d)", fieldNum, wire)
|
||||
}
|
||||
switch fieldNum {
|
||||
case 1:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field PublicKey", wireType)
|
||||
}
|
||||
var byteLen int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowIdentify
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
byteLen |= int(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if byteLen < 0 {
|
||||
return ErrInvalidLengthIdentify
|
||||
}
|
||||
postIndex := iNdEx + byteLen
|
||||
if postIndex < 0 {
|
||||
return ErrInvalidLengthIdentify
|
||||
}
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.PublicKey = append(m.PublicKey[:0], dAtA[iNdEx:postIndex]...)
|
||||
if m.PublicKey == nil {
|
||||
m.PublicKey = []byte{}
|
||||
}
|
||||
iNdEx = postIndex
|
||||
case 2:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field ListenAddrs", wireType)
|
||||
}
|
||||
var byteLen int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowIdentify
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
byteLen |= int(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if byteLen < 0 {
|
||||
return ErrInvalidLengthIdentify
|
||||
}
|
||||
postIndex := iNdEx + byteLen
|
||||
if postIndex < 0 {
|
||||
return ErrInvalidLengthIdentify
|
||||
}
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.ListenAddrs = append(m.ListenAddrs, make([]byte, postIndex-iNdEx))
|
||||
copy(m.ListenAddrs[len(m.ListenAddrs)-1], dAtA[iNdEx:postIndex])
|
||||
iNdEx = postIndex
|
||||
case 3:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Protocols", wireType)
|
||||
}
|
||||
var stringLen uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowIdentify
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
stringLen |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
intStringLen := int(stringLen)
|
||||
if intStringLen < 0 {
|
||||
return ErrInvalidLengthIdentify
|
||||
}
|
||||
postIndex := iNdEx + intStringLen
|
||||
if postIndex < 0 {
|
||||
return ErrInvalidLengthIdentify
|
||||
}
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.Protocols = append(m.Protocols, string(dAtA[iNdEx:postIndex]))
|
||||
iNdEx = postIndex
|
||||
case 4:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field ObservedAddr", wireType)
|
||||
}
|
||||
var byteLen int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowIdentify
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
byteLen |= int(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if byteLen < 0 {
|
||||
return ErrInvalidLengthIdentify
|
||||
}
|
||||
postIndex := iNdEx + byteLen
|
||||
if postIndex < 0 {
|
||||
return ErrInvalidLengthIdentify
|
||||
}
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.ObservedAddr = append(m.ObservedAddr[:0], dAtA[iNdEx:postIndex]...)
|
||||
if m.ObservedAddr == nil {
|
||||
m.ObservedAddr = []byte{}
|
||||
}
|
||||
iNdEx = postIndex
|
||||
case 5:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field ProtocolVersion", wireType)
|
||||
}
|
||||
var stringLen uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowIdentify
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
stringLen |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
intStringLen := int(stringLen)
|
||||
if intStringLen < 0 {
|
||||
return ErrInvalidLengthIdentify
|
||||
}
|
||||
postIndex := iNdEx + intStringLen
|
||||
if postIndex < 0 {
|
||||
return ErrInvalidLengthIdentify
|
||||
}
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
s := string(dAtA[iNdEx:postIndex])
|
||||
m.ProtocolVersion = &s
|
||||
iNdEx = postIndex
|
||||
case 6:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field AgentVersion", wireType)
|
||||
}
|
||||
var stringLen uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowIdentify
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
stringLen |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
intStringLen := int(stringLen)
|
||||
if intStringLen < 0 {
|
||||
return ErrInvalidLengthIdentify
|
||||
}
|
||||
postIndex := iNdEx + intStringLen
|
||||
if postIndex < 0 {
|
||||
return ErrInvalidLengthIdentify
|
||||
}
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
s := string(dAtA[iNdEx:postIndex])
|
||||
m.AgentVersion = &s
|
||||
iNdEx = postIndex
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipIdentify(dAtA[iNdEx:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthIdentify
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthIdentify
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
|
||||
iNdEx += skippy
|
||||
}
|
||||
}
|
||||
|
||||
if iNdEx > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func skipIdentify(dAtA []byte) (n int, err error) {
|
||||
l := len(dAtA)
|
||||
iNdEx := 0
|
||||
for iNdEx < l {
|
||||
var wire uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return 0, ErrIntOverflowIdentify
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
wire |= (uint64(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
wireType := int(wire & 0x7)
|
||||
switch wireType {
|
||||
case 0:
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return 0, ErrIntOverflowIdentify
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
iNdEx++
|
||||
if dAtA[iNdEx-1] < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return iNdEx, nil
|
||||
case 1:
|
||||
iNdEx += 8
|
||||
return iNdEx, nil
|
||||
case 2:
|
||||
var length int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return 0, ErrIntOverflowIdentify
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
length |= (int(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if length < 0 {
|
||||
return 0, ErrInvalidLengthIdentify
|
||||
}
|
||||
iNdEx += length
|
||||
if iNdEx < 0 {
|
||||
return 0, ErrInvalidLengthIdentify
|
||||
}
|
||||
return iNdEx, nil
|
||||
case 3:
|
||||
for {
|
||||
var innerWire uint64
|
||||
var start int = iNdEx
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return 0, ErrIntOverflowIdentify
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
innerWire |= (uint64(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
innerWireType := int(innerWire & 0x7)
|
||||
if innerWireType == 4 {
|
||||
break
|
||||
}
|
||||
next, err := skipIdentify(dAtA[start:])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
iNdEx = start + next
|
||||
if iNdEx < 0 {
|
||||
return 0, ErrInvalidLengthIdentify
|
||||
}
|
||||
}
|
||||
return iNdEx, nil
|
||||
case 4:
|
||||
return iNdEx, nil
|
||||
case 5:
|
||||
iNdEx += 4
|
||||
return iNdEx, nil
|
||||
default:
|
||||
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
|
||||
}
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
var (
|
||||
ErrInvalidLengthIdentify = fmt.Errorf("proto: negative length found during unmarshaling")
|
||||
ErrIntOverflowIdentify = fmt.Errorf("proto: integer overflow")
|
||||
)
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
syntax = "proto2";
|
||||
|
||||
package identify.pb;
|
||||
|
||||
message Identify {
|
||||
|
||||
// protocolVersion determines compatibility between peers
|
||||
optional string protocolVersion = 5; // e.g. ipfs/1.0.0
|
||||
|
||||
// agentVersion is like a UserAgent string in browsers, or client version in bittorrent
|
||||
// includes the client name and client.
|
||||
optional string agentVersion = 6; // e.g. go-ipfs/0.1.0
|
||||
|
||||
// publicKey is this node's public key (which also gives its node.ID)
|
||||
// - may not need to be sent, as secure channel implies it has been sent.
|
||||
// - then again, if we change / disable secure channel, may still want it.
|
||||
optional bytes publicKey = 1;
|
||||
|
||||
// listenAddrs are the multiaddrs the sender node listens for open connections on
|
||||
repeated bytes listenAddrs = 2;
|
||||
|
||||
// oservedAddr is the multiaddr of the remote endpoint that the sender node perceives
|
||||
// this is useful information to convey to the other side, as it helps the remote endpoint
|
||||
// determine whether its connection to the local peer goes through NAT.
|
||||
optional bytes observedAddr = 4;
|
||||
|
||||
// protocols are the services this node is running
|
||||
repeated string protocols = 3;
|
||||
}
|
||||
+133
@@ -0,0 +1,133 @@
|
||||
package ping
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
u "github.com/ipfs/go-ipfs-util"
|
||||
logging "github.com/ipfs/go-log"
|
||||
host "github.com/libp2p/go-libp2p-host"
|
||||
inet "github.com/libp2p/go-libp2p-net"
|
||||
peer "github.com/libp2p/go-libp2p-peer"
|
||||
)
|
||||
|
||||
var log = logging.Logger("ping")
|
||||
|
||||
const PingSize = 32
|
||||
|
||||
const ID = "/ipfs/ping/1.0.0"
|
||||
|
||||
const pingTimeout = time.Second * 60
|
||||
|
||||
type PingService struct {
|
||||
Host host.Host
|
||||
}
|
||||
|
||||
func NewPingService(h host.Host) *PingService {
|
||||
ps := &PingService{h}
|
||||
h.SetStreamHandler(ID, ps.PingHandler)
|
||||
return ps
|
||||
}
|
||||
|
||||
func (p *PingService) PingHandler(s inet.Stream) {
|
||||
buf := make([]byte, PingSize)
|
||||
|
||||
errCh := make(chan error, 1)
|
||||
defer close(errCh)
|
||||
timer := time.NewTimer(pingTimeout)
|
||||
defer timer.Stop()
|
||||
|
||||
go func() {
|
||||
select {
|
||||
case <-timer.C:
|
||||
log.Debug("ping timeout")
|
||||
case err, ok := <-errCh:
|
||||
if ok {
|
||||
log.Debug(err)
|
||||
} else {
|
||||
log.Error("ping loop failed without error")
|
||||
}
|
||||
}
|
||||
s.Reset()
|
||||
}()
|
||||
|
||||
for {
|
||||
_, err := io.ReadFull(s, buf)
|
||||
if err != nil {
|
||||
errCh <- err
|
||||
return
|
||||
}
|
||||
|
||||
_, err = s.Write(buf)
|
||||
if err != nil {
|
||||
errCh <- err
|
||||
return
|
||||
}
|
||||
|
||||
timer.Reset(pingTimeout)
|
||||
}
|
||||
}
|
||||
|
||||
func (ps *PingService) Ping(ctx context.Context, p peer.ID) (<-chan time.Duration, error) {
|
||||
return Ping(ctx, ps.Host, p)
|
||||
}
|
||||
|
||||
func Ping(ctx context.Context, h host.Host, p peer.ID) (<-chan time.Duration, error) {
|
||||
s, err := h.NewStream(ctx, p, ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
out := make(chan time.Duration)
|
||||
go func() {
|
||||
defer close(out)
|
||||
defer s.Reset()
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
default:
|
||||
t, err := ping(s)
|
||||
if err != nil {
|
||||
log.Debugf("ping error: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
h.Peerstore().RecordLatency(p, t)
|
||||
select {
|
||||
case out <- t:
|
||||
case <-ctx.Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func ping(s inet.Stream) (time.Duration, error) {
|
||||
buf := make([]byte, PingSize)
|
||||
u.NewTimeSeededRand().Read(buf)
|
||||
|
||||
before := time.Now()
|
||||
_, err := s.Write(buf)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
rbuf := make([]byte, PingSize)
|
||||
_, err = io.ReadFull(s, rbuf)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if !bytes.Equal(buf, rbuf) {
|
||||
return 0, errors.New("ping packet was incorrect!")
|
||||
}
|
||||
|
||||
return time.Since(before), nil
|
||||
}
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
{
|
||||
"columns": [
|
||||
"Name",
|
||||
"CI/Travis",
|
||||
"Coverage",
|
||||
"Description"
|
||||
],
|
||||
"rows": [
|
||||
"Libp2p",
|
||||
["libp2p/go-libp2p", "go-libp2p", "go-libp2p entry point"],
|
||||
["libp2p/go-libp2p-host", "go-libp2p-host", "libp2p \"host\" interface"],
|
||||
["libp2p/go-libp2p-blankhost", "go-libp2p-blankhost", "minimal implementation of the \"host\" interface"],
|
||||
|
||||
"Network",
|
||||
["libp2p/go-libp2p-net", "go-libp2p-net", "libp2p connection and \"network\" interfaces"],
|
||||
["libp2p/go-libp2p-swarm", "go-libp2p-swarm", "reference implementation"],
|
||||
|
||||
"Transport",
|
||||
["libp2p/go-libp2p-transport", "go-libp2p-transport", "interfaces"],
|
||||
["libp2p/go-ws-transport", "go-ws-transport", "WebSocket transport"],
|
||||
["libp2p/go-tcp-transport", "go-tcp-transport", "TCP transport"],
|
||||
["libp2p/go-libp2p-quic-transport", "go-libp2p-quic-transport", "QUIC transport"],
|
||||
["libp2p/go-udp-transport", "go-udp-transport", "UDP transport"],
|
||||
["libp2p/go-utp-transport", "go-utp-transport", "uTorrent transport (UTP)"],
|
||||
["libp2p/go-libp2p-circuit", "go-libp2p-circuit", "relay transport"],
|
||||
["libp2p/go-libp2p-transport-upgrader", "go-libp2p-transport-upgrader", "upgrades multiaddr-net connections into full libp2p transports"],
|
||||
["libp2p/go-libp2p-reuseport-transport", "go-libp2p-reuseport-transport", "partial transport for building transports that reuse ports"],
|
||||
|
||||
"Encrypted Channels",
|
||||
["libp2p/go-conn-security", "go-conn-security", "interfaces"],
|
||||
["libp2p/go-libp2p-secio", "go-libp2p-secio", "SecIO crypto channel"],
|
||||
["libp2p/go-conn-security-multistream", "go-conn-security-multistream", "multistream multiplexed meta crypto channel"],
|
||||
|
||||
"Private Network",
|
||||
["libp2p/go-libp2p-interface-pnet", "go-libp2p-interface-pnet", "interfaces"],
|
||||
["libp2p/go-libp2p-pnet", "go-libp2p-pnet", "reference implementation"],
|
||||
|
||||
"Stream Muxers",
|
||||
["libp2p/go-stream-muxer", "go-stream-muxer", "interfaces"],
|
||||
["whyrusleeping/go-smux-yamux", "go-smux-yamux", "YAMUX stream multiplexer"],
|
||||
["whyrusleeping/go-smux-mplex", "go-smux-mplex", "MPLEX stream multiplexer"],
|
||||
|
||||
"NAT Traversal",
|
||||
["libp2p/go-libp2p-nat", "go-libp2p-nat"],
|
||||
|
||||
"Peerstore",
|
||||
["libp2p/go-libp2p-peerstore", "go-libp2p-peerstore", "interfaces and reference implementation"],
|
||||
|
||||
"Connection Manager",
|
||||
["libp2p/go-libp2p-interface-connmgr", "go-libp2p-interface-connmgr", "interface"],
|
||||
["libp2p/go-libp2p-connmgr", "go-libp2p-connmgr", "reference implementation"],
|
||||
|
||||
"Routing",
|
||||
["libp2p/go-libp2p-routing", "go-libp2p-routing", "routing interfaces"],
|
||||
["libp2p/go-libp2p-record", "go-libp2p-record", "record type and validator logic"],
|
||||
["libp2p/go-libp2p-routing-helpers", "go-libp2p-routing-helpers", "helpers for composing routers"],
|
||||
["libp2p/go-libp2p-kad-dht", "go-libp2p-kad-dht", "Kademlia-like router"],
|
||||
["libp2p/go-libp2p-pubsub-router", "go-libp2p-pubsub-router", "record-store over pubsub adapter"],
|
||||
|
||||
"Consensus",
|
||||
["libp2p/go-libp2p-consensus", "go-libp2p-consensus", "consensus protocols interfaces"],
|
||||
["libp2p/go-libp2p-raft", "go-libp2p-raft", "consensus implementation over raft"],
|
||||
|
||||
"Pubsub",
|
||||
["libp2p/go-libp2p-pubsub", "go-libp2p-pubsub", "multiple pubsub over libp2p implementations"],
|
||||
|
||||
"RPC",
|
||||
["libp2p/go-libp2p-gorpc", "go-libp2p-gorpc", "a simple RPC library for libp2p"],
|
||||
|
||||
"Metrics",
|
||||
["libp2p/go-libp2p-metrics", "go-libp2p-metrics", "libp2p metrics interfaces/collectors"],
|
||||
|
||||
"Data Types",
|
||||
["libp2p/go-libp2p-peer", "go-libp2p-peer", "libp2p peer-ID datatype"],
|
||||
["libp2p/go-libp2p-crypto", "go-libp2p-crypto", "libp2p key types"],
|
||||
["libp2p/go-libp2p-protocol", "go-libp2p-protocol", "libp2p protocol datatype"],
|
||||
["libp2p/go-libp2p-kbucket", "go-libp2p-kbucket", "Kademlia routing table helper types"],
|
||||
|
||||
"Utilities/miscellaneous",
|
||||
["libp2p/go-libp2p-loggables", "go-libp2p-loggables", "logging helpers"],
|
||||
["libp2p/go-maddr-filter", "go-maddr-filter", "multiaddr filtering helpers"],
|
||||
["libp2p/go-libp2p-netutil", "go-libp2p-netutil", "misc utilities"],
|
||||
["libp2p/go-msgio", "go-msgio", "length prefixed data channel"],
|
||||
["libp2p/go-addr-util", "go-addr-util", "address utilities for libp2p swarm"],
|
||||
["libp2p/go-buffer-pool", "go-buffer-pool", "a variable size buffer pool for go"],
|
||||
["libp2p/go-libp2p-routing-helpers", "go-libp2p-routing-helpers", "routing helpers"],
|
||||
["libp2p/go-reuseport", "go-reuseport", "enables reuse of addresses"],
|
||||
["libp2p/go-sockaddr", "go-sockaddr", "utils for sockaddr conversions"],
|
||||
["libp2p/go-flow-metrics", "go-flow-metrics", "metrics library"],
|
||||
|
||||
"Testing and examples",
|
||||
["libp2p/go-testutil", "go-testutil", "a collection of testing utilities for ipfs and libp2p"],
|
||||
["libp2p/go-libp2p-examples", "go-libp2p-examples", "go-libp2p examples and tutorials"],
|
||||
["libp2p/go-libp2p-circuit-progs", "go-libp2p-circuit-progs", "testing programs for go-libp2p-circuit"]
|
||||
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user