Commit 41c68348 authored by Steven Allen's avatar Steven Allen
Browse files

refactor for transport changes

Also, make the libp2p constructor fully useful. There should now be no need to
manually construct a swarm/host.
parent 4b33a800
package config
import (
"context"
"fmt"
bhost "github.com/libp2p/go-libp2p/p2p/host/basic"
logging "github.com/ipfs/go-log"
circuit "github.com/libp2p/go-libp2p-circuit"
crypto "github.com/libp2p/go-libp2p-crypto"
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"
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
// 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
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
}
// 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 {
cfg.Peerstore.AddPrivKey(pid, cfg.PeerKey)
cfg.Peerstore.AddPubKey(pid, cfg.PeerKey.GetPublic())
}
// TODO: Make the swarm implementation configurable.
swrm := swarm.NewSwarm(ctx, pid, cfg.Peerstore, cfg.Reporter)
if cfg.Filters != nil {
swrm.Filters = cfg.Filters
}
// TODO: make host implementation configurable.
h, err := bhost.NewHost(ctx, swrm, &bhost.HostOpts{
ConnManager: cfg.ConnManager,
AddrsFactory: cfg.AddrsFactory,
NATManager: cfg.NATManager,
})
if err != nil {
swrm.Close()
return nil, err
}
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
}
// TODO: Configure routing (it's a pain to setup).
// TODO: Bootstrapping.
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 err := opt(cfg); err != nil {
return err
}
}
return nil
}
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
}
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)
}
}
for _, tptC := range tpts {
tpt, err := tptC.MuxC(h)
if err != nil {
return nil, err
}
muxMuxer.AddTransport(tptC.ID, tpt)
}
return muxMuxer, nil
}
package config
import (
"testing"
peer "github.com/libp2p/go-libp2p-peer"
mux "github.com/libp2p/go-stream-muxer"
yamux "github.com/whyrusleeping/go-smux-yamux"
)
func TestMuxerSimple(t *testing.T) {
// single
_, err := MuxerConstructor(func(_ peer.ID) mux.Transport { return nil })
if err != nil {
t.Fatal(err)
}
}
func TestMuxerByValue(t *testing.T) {
_, err := MuxerConstructor(yamux.DefaultTransport)
if err != nil {
t.Fatal(err)
}
}
func TestMuxerDuplicate(t *testing.T) {
_, err := MuxerConstructor(func(_ peer.ID, _ peer.ID) mux.Transport { return nil })
if err != nil {
t.Fatal(err)
}
}
func TestMuxerError(t *testing.T) {
_, err := MuxerConstructor(func() (mux.Transport, error) { return nil, nil })
if err != nil {
t.Fatal(err)
}
}
func TestMuxerBadTypes(t *testing.T) {
for i, f := range []interface{}{
func() error { return nil },
func() string { return "" },
func() {},
func(string) mux.Transport { return nil },
func(string) (mux.Transport, error) { return nil, nil },
nil,
"testing",
} {
if _, err := MuxerConstructor(f); err == nil {
t.Fatalf("constructor %d with type %T should have failed", i, f)
}
}
}
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("expected first return value from transport constructor to be a transport")
}
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
}
package config
import (
"errors"
"reflect"
"strings"
"testing"
)
func TestHandleReturnValue(t *testing.T) {
// one value
v, err := handleReturnValue([]reflect.Value{reflect.ValueOf(1)})
if v.(int) != 1 {
t.Fatal("expected value")
}
if err != nil {
t.Fatal(err)
}
// Nil value
v, err = handleReturnValue([]reflect.Value{reflect.ValueOf(nil)})
if v != nil {
t.Fatal("expected no value")
}
if err == nil {
t.Fatal("expected an error")
}
// Nil value, nil err
v, err = handleReturnValue([]reflect.Value{reflect.ValueOf(nil), reflect.ValueOf(nil)})
if v != nil {
t.Fatal("expected no value")
}
if err == nil {
t.Fatal("expected an error")
}
// two values
v, err = handleReturnValue([]reflect.Value{reflect.ValueOf(1), reflect.ValueOf(nil)})
if v, ok := v.(int); !ok || v != 1 {
t.Fatalf("expected value of 1, got %v", v)
}
if err != nil {
t.Fatal("expected no error")
}
// an error
myError := errors.New("my error")
_, err = handleReturnValue([]reflect.Value{reflect.ValueOf(1), reflect.ValueOf(myError)})
if err != myError {
t.Fatal(err)
}
for _, vals := range [][]reflect.Value{
{reflect.ValueOf(1), reflect.ValueOf("not an error")},
{},
{reflect.ValueOf(1), reflect.ValueOf(myError), reflect.ValueOf(myError)},
} {
func() {
defer func() { recover() }()
handleReturnValue(vals)
t.Fatal("expected a panic")
}()
}
}
type foo interface {
foo() foo
}
var fooType = reflect.TypeOf((*foo)(nil)).Elem()
func TestCheckReturnType(t *testing.T) {
for i, fn := range []interface{}{
func() { panic("") },
func() error { panic("") },
func() (error, error) { panic("") },
func() (foo, error, error) { panic("") },
func() (foo, foo) { panic("") },
} {
if checkReturnType(reflect.TypeOf(fn), fooType) == nil {
t.Errorf("expected falure for case %d (type %T)", i, fn)
}
}
for i, fn := range []interface{}{
func() foo { panic("") },
func() (foo, error) { panic("") },
} {
if err := checkReturnType(reflect.TypeOf(fn), fooType); err != nil {
t.Errorf("expected success for case %d (type %T), got: %s", i, fn, err)
}
}
}
func constructFoo() foo {
return nil
}
type fooImpl struct{}
func (f *fooImpl) foo() foo { return nil }
func TestCallConstructor(t *testing.T) {
_, err := callConstructor(reflect.ValueOf(constructFoo), nil)
if err == nil {
t.Fatal("expected constructor to fail")
}
if !strings.Contains(err.Error(), "constructFoo") {
t.Errorf("expected error to contain the constructor name: %s", err)
}
v, err := callConstructor(reflect.ValueOf(func() foo { return &fooImpl{} }), nil)
if err != nil {
t.Fatal(err)
}
if _, ok := v.(*fooImpl); !ok {
t.Fatal("expected a fooImpl")
}
v, err = callConstructor(reflect.ValueOf(func() *fooImpl { return new(fooImpl) }), nil)
if err != nil {
t.Fatal(err)
}
if _, ok := v.(*fooImpl); !ok {
t.Fatal("expected a fooImpl")
}
_, err = callConstructor(reflect.ValueOf(func() (*fooImpl, error) { return nil, nil }), nil)
if err == nil {
t.Fatal("expected error")
}
v, err = callConstructor(reflect.ValueOf(func() (*fooImpl, error) { return new(fooImpl), nil }), nil)
if err != nil {
t.Fatal(err)
}
if _, ok := v.(*fooImpl); !ok {
t.Fatal("expected a fooImpl")
}
}
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)
}
}
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
}
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
}
package libp2p
// This file contains all the default configuration options.
import (
"crypto/rand"
crypto "github.com/libp2p/go-libp2p-crypto"
pstore "github.com/libp2p/go-libp2p-peerstore"
secio "github.com/libp2p/go-libp2p-secio"
tcp "github.com/libp2p/go-tcp-transport"
ws "github.com/libp2p/go-ws-transport"
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)
// DefaultMuxer 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.3.0", mplex.DefaultTransport),
)
// DefaultTransports are the default libp2p transports.
//
// Use this option when you want to *extend* the set of multiplexers 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(pstore.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))
}
// 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 },
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,
},
}
// 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
}
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)
}
......@@ -39,13 +39,13 @@ import (
mrand "math/rand"
"os"
"github.com/libp2p/go-libp2p"
"github.com/libp2p/go-libp2p-crypto"
"github.com/libp2p/go-libp2p-host"
"github.com/libp2p/go-libp2p-net"
"github.com/libp2p/go-libp2p-peer"
"github.com/libp2p/go-libp2p-peerstore"
"github.com/libp2p/go-libp2p-swarm"
"github.com/libp2p/go-libp2p/p2p/host/basic"
"github.com/multiformats/go-multiaddr"
)
......@@ -155,37 +155,27 @@ func main() {
}
// Creates a new RSA key pair for this host
prvKey, pubKey, err := crypto.GenerateKeyPairWithReader(crypto.RSA, 2048, r)
prvKey, _, err := crypto.GenerateKeyPairWithReader(crypto.RSA, 2048, r)
if err != nil {
panic(err)
}
// Getting host ID from public key.
// host ID is the hash of public key
nodeID, _ := peer.IDFromPublicKey(pubKey)
// 0.0.0.0 will listen on any interface device
sourceMultiAddr, _ := multiaddr.NewMultiaddr(fmt.Sprintf("/ip4/0.0.0.0/tcp/%d", *sourcePort))
// Adding self to the peerstore.
ps := peerstore.NewPeerstore()
ps.AddPrivKey(nodeID, prvKey)
ps.AddPubKey(nodeID, pubKey)
// Creating a new Swarm network.
network, err := swarm.NewNetwork(context.Background(), []multiaddr.Multiaddr{sourceMultiAddr}, nodeID, ps, nil)
// libp2p.New constructs a new libp2p Host.
// Other options can be added here.
host, err := libp2p.New(
context.Background(),
libp2p.ListenAddrs(sourceMultiAddr),
libp2p.Identity(prvKey),
)
if err != nil {
panic(err)
}
// NewHost constructs a new *BasicHost and activates it by attaching its
// stream and connection handlers to the given inet.Network (network).
// Other options like NATManager can also be added here.
// See docs: https://godoc.org/github.com/libp2p/go-libp2p/p2p/host/basic#HostOpts
host := basichost.New(network)
if *dest == "" {
// Set a function as stream handler.
// This function is called when a peer initiate a connection and starts a stream with this peer.
......
......@@ -49,7 +49,7 @@ func makeBasicHost(listenPort int, secio bool, randseed int64) (host.Host, error
}
if !secio {
opts = append(opts, libp2p.NoEncryption())
opts = append(opts, libp2p.NoSecurity)
}
basicHost, err := libp2p.New(context.Background(), opts...)
......
......@@ -12,16 +12,14 @@ import (
// We need to import libp2p's libraries that we use in this project.
// In order to work, these libraries need to be rewritten by gx-go.
crypto "github.com/libp2p/go-libp2p-crypto"
host "github.com/libp2p/go-libp2p-host"
inet "github.com/libp2p/go-libp2p-net"
peer "github.com/libp2p/go-libp2p-peer"
ps "github.com/libp2p/go-libp2p-peerstore"
swarm "github.com/libp2p/go-libp2p-swarm"
ma "github.com/multiformats/go-multiaddr"
manet "github.com/multiformats/go-multiaddr-net"
bhost "github.com/libp2p/go-libp2p/p2p/host/basic"
libp2p "github.com/libp2p/go-libp2p"
)
// Protocol defines the libp2p protocol that we will use for the libp2p proxy
......@@ -33,27 +31,11 @@ const Protocol = "/proxy-example/0.0.1"
// makeRandomHost creates a libp2p host with a randomly generated identity.
// This step is described in depth in other tutorials.
func makeRandomHost(port int) host.Host {
priv, pub, err := crypto.GenerateKeyPair(crypto.RSA, 2048)
host, err := libp2p.New(context.Background(), libp2p.ListenAddrStrings(fmt.Sprintf("/ip4/127.0.0.1/tcp/%d", port)))
if err != nil {
log.Fatalln(err)
}
pid, err := peer.IDFromPublicKey(pub)
if err != nil {
log.Fatalln(err)
}
listen, err := ma.NewMultiaddr(fmt.Sprintf("/ip4/127.0.0.1/tcp/%d", port))
if err != nil {
log.Fatalln(err)
}
ps := ps.NewPeerstore()
ps.AddPrivKey(pid, priv)
ps.AddPubKey(pid, pub)
n, err := swarm.NewNetwork(context.Background(),
[]ma.Multiaddr{listen}, pid, ps, nil)
if err != nil {
log.Fatalln(err)
}
return bhost.New(n)
return host
}
// ProxyService provides HTTP proxying on top of libp2p by launching an
......
......@@ -6,11 +6,9 @@ import (
"log"
"math/rand"
libp2p "github.com/libp2p/go-libp2p"
crypto "github.com/libp2p/go-libp2p-crypto"
peer "github.com/libp2p/go-libp2p-peer"
ps "github.com/libp2p/go-libp2p-peerstore"
swarm "github.com/libp2p/go-libp2p-swarm"
bhost "github.com/libp2p/go-libp2p/p2p/host/basic"
ma "github.com/multiformats/go-multiaddr"
)
......@@ -18,14 +16,13 @@ import (
func makeRandomNode(port int, done chan bool) *Node {
// Ignoring most errors for brevity
// See echo example for more details and better implementation
priv, pub, _ := crypto.GenerateKeyPair(crypto.Secp256k1, 256)
pid, _ := peer.IDFromPublicKey(pub)
priv, _, _ := crypto.GenerateKeyPair(crypto.Secp256k1, 256)
listen, _ := ma.NewMultiaddr(fmt.Sprintf("/ip4/127.0.0.1/tcp/%d", port))
peerStore := ps.NewPeerstore()
peerStore.AddPrivKey(pid, priv)
peerStore.AddPubKey(pid, pub)
n, _ := swarm.NewNetwork(context.Background(), []ma.Multiaddr{listen}, pid, peerStore, nil)
host := bhost.New(n)
host, _ := libp2p.New(
context.Background(),
libp2p.ListenAddrs(listen),
libp2p.Identity(priv),
)
return NewNode(host, done)
}
......
......@@ -8,15 +8,11 @@ import (
"math/rand"
"time"
crypto "github.com/libp2p/go-libp2p-crypto"
host "github.com/libp2p/go-libp2p-host"
inet "github.com/libp2p/go-libp2p-net"
peer "github.com/libp2p/go-libp2p-peer"
ps "github.com/libp2p/go-libp2p-peerstore"
swarm "github.com/libp2p/go-libp2p-swarm"
ma "github.com/multiformats/go-multiaddr"
bhost "github.com/libp2p/go-libp2p/p2p/host/basic"
libp2p "github.com/libp2p/go-libp2p"
multicodec "github.com/multiformats/go-multicodec"
json "github.com/multiformats/go-multicodec/json"
)
......@@ -80,17 +76,11 @@ var conversationMsgs = []string{
}
func makeRandomHost(port int) host.Host {
// Ignoring most errors for brevity
// See echo example for more details and better implementation
priv, pub, _ := crypto.GenerateKeyPair(crypto.RSA, 2048)
pid, _ := peer.IDFromPublicKey(pub)
listen, _ := ma.NewMultiaddr(fmt.Sprintf("/ip4/127.0.0.1/tcp/%d", port))
ps := ps.NewPeerstore()
ps.AddPrivKey(pid, priv)
ps.AddPubKey(pid, pub)
n, _ := swarm.NewNetwork(context.Background(),
[]ma.Multiaddr{listen}, pid, ps, nil)
return bhost.New(n)
h, err := libp2p.New(context.Background(), libp2p.ListenAddrStrings(fmt.Sprintf("/ip4/127.0.0.1/tcp/%d", port)))
if err != nil {
panic(err)
}
return h
}
func main() {
......
......@@ -2,240 +2,49 @@ package libp2p
import (
"context"
"crypto/rand"
"fmt"
crypto "github.com/libp2p/go-libp2p-crypto"
config "github.com/libp2p/go-libp2p/config"
host "github.com/libp2p/go-libp2p-host"
pnet "github.com/libp2p/go-libp2p-interface-pnet"
metrics "github.com/libp2p/go-libp2p-metrics"
peer "github.com/libp2p/go-libp2p-peer"
pstore "github.com/libp2p/go-libp2p-peerstore"
swarm "github.com/libp2p/go-libp2p-swarm"
transport "github.com/libp2p/go-libp2p-transport"
bhost "github.com/libp2p/go-libp2p/p2p/host/basic"
mux "github.com/libp2p/go-stream-muxer"
ma "github.com/multiformats/go-multiaddr"
mplex "github.com/whyrusleeping/go-smux-multiplex"
msmux "github.com/whyrusleeping/go-smux-multistream"
yamux "github.com/whyrusleeping/go-smux-yamux"
)
// Config describes a set of settings for a libp2p node
type Config struct {
Transports []transport.Transport
Muxer mux.Transport
ListenAddrs []ma.Multiaddr
PeerKey crypto.PrivKey
Peerstore pstore.Peerstore
Protector pnet.Protector
Reporter metrics.Reporter
DisableSecio bool
EnableNAT bool
}
type Option func(cfg *Config) error
type Config = config.Config
func Transports(tpts ...transport.Transport) Option {
return func(cfg *Config) error {
cfg.Transports = append(cfg.Transports, tpts...)
return nil
}
}
// Option is a libp2p config option that can be given to the libp2p constructor
// (`libp2p.New`).
type Option = config.Option
func ListenAddrStrings(s ...string) Option {
// ChainOptions chains multiple options into a single option.
func ChainOptions(opts ...Option) Option {
return func(cfg *Config) error {
for _, addrstr := range s {
a, err := ma.NewMultiaddr(addrstr)
if err != nil {
for _, opt := range opts {
if err := opt(cfg); err != nil {
return err
}
cfg.ListenAddrs = append(cfg.ListenAddrs, a)
}
return nil
}
}
func ListenAddrs(addrs ...ma.Multiaddr) Option {
return func(cfg *Config) error {
cfg.ListenAddrs = append(cfg.ListenAddrs, addrs...)
return nil
}
}
type transportEncOpt int
const (
EncPlaintext = transportEncOpt(0)
EncSecio = transportEncOpt(1)
)
func TransportEncryption(tenc ...transportEncOpt) Option {
return func(cfg *Config) error {
if len(tenc) != 1 {
return fmt.Errorf("can only specify a single transport encryption option right now")
}
// TODO: actually make this pluggable, otherwise tls will get tricky
switch tenc[0] {
case EncPlaintext:
cfg.DisableSecio = true
case EncSecio:
// noop
default:
return fmt.Errorf("unrecognized transport encryption option: %d", tenc[0])
}
return nil
}
}
func NoEncryption() Option {
return TransportEncryption(EncPlaintext)
}
func NATPortMap() Option {
return func(cfg *Config) error {
cfg.EnableNAT = true
return nil
}
}
func Muxer(m mux.Transport) Option {
return func(cfg *Config) error {
if cfg.Muxer != nil {
return fmt.Errorf("cannot specify multiple muxer options")
}
cfg.Muxer = m
return nil
}
}
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
}
}
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
}
}
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
}
}
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
}
}
// New constructs a new libp2p node with the given options (falling back on
// reasonable defaults).
//
// Canceling the passed context will stop the returned libp2p node.
func New(ctx context.Context, opts ...Option) (host.Host, error) {
var cfg Config
for _, opt := range opts {
if err := opt(&cfg); err != nil {
return nil, err
}
}
return newWithCfg(ctx, &cfg)
return NewWithoutDefaults(ctx, append(opts, FallbackDefaults)...)
}
func newWithCfg(ctx context.Context, cfg *Config) (host.Host, error) {
// If no key was given, generate a random 2048 bit RSA key
if cfg.PeerKey == nil {
priv, _, err := crypto.GenerateKeyPairWithReader(crypto.RSA, 2048, rand.Reader)
if err != nil {
return nil, err
}
cfg.PeerKey = priv
}
// Obtain Peer ID from public key
pid, err := peer.IDFromPublicKey(cfg.PeerKey.GetPublic())
if err != nil {
return nil, err
}
// Create a new blank peerstore if none was passed in
ps := cfg.Peerstore
if ps == nil {
ps = pstore.NewPeerstore()
}
// Set default muxer if none was passed in
muxer := cfg.Muxer
if muxer == nil {
muxer = DefaultMuxer()
}
// If secio is disabled, don't add our private key to the peerstore
if !cfg.DisableSecio {
ps.AddPrivKey(pid, cfg.PeerKey)
ps.AddPubKey(pid, cfg.PeerKey.GetPublic())
}
swrm, err := swarm.NewSwarmWithProtector(ctx, cfg.ListenAddrs, pid, ps, cfg.Protector, muxer, cfg.Reporter)
if err != nil {
// 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
}
netw := (*swarm.Network)(swrm)
hostOpts := &bhost.HostOpts{}
if cfg.EnableNAT {
hostOpts.NATManager = bhost.NewNATManager(netw)
}
return bhost.NewHost(ctx, netw, hostOpts)
}
func DefaultMuxer() mux.Transport {
// Set up stream multiplexer
tpt := msmux.NewBlankTransport()
// By default, support yamux and multiplex
tpt.AddTransport("/yamux/1.0.0", yamux.DefaultTransport)
tpt.AddTransport("/mplex/6.3.0", mplex.DefaultTransport)
return tpt
}
func Defaults(cfg *Config) error {
// Create a multiaddress that listens on a random port on all interfaces
addr, err := ma.NewMultiaddr("/ip4/0.0.0.0/tcp/0")
if err != nil {
return err
}
cfg.ListenAddrs = []ma.Multiaddr{addr}
cfg.Peerstore = pstore.NewPeerstore()
cfg.Muxer = DefaultMuxer()
return nil
return cfg.NewNode(ctx)
}
......@@ -3,6 +3,7 @@ package libp2p
import (
"context"
"fmt"
"strings"
"testing"
crypto "github.com/libp2p/go-libp2p-crypto"
......@@ -10,10 +11,32 @@ import (
)
func TestNewHost(t *testing.T) {
_, err := makeRandomHost(t, 9000)
h, err := makeRandomHost(t, 9000)
if err != nil {
t.Fatal(err)
}
h.Close()
}
func TestBadTransportConstructor(t *testing.T) {
ctx := context.Background()
h, err := New(ctx, Transport(func() {}))
if err == nil {
h.Close()
t.Fatal("expected an error")
}
if !strings.Contains(err.Error(), "libp2p_test.go") {
t.Error("expected error to contain debugging info")
}
}
func TestInsecure(t *testing.T) {
ctx := context.Background()
h, err := New(ctx, NoSecurity)
if err != nil {
t.Fatal(err)
}
h.Close()
}
func makeRandomHost(t *testing.T, port int) (host.Host, error) {
......@@ -26,7 +49,9 @@ func makeRandomHost(t *testing.T, port int) (host.Host, error) {
opts := []Option{
ListenAddrStrings(fmt.Sprintf("/ip4/127.0.0.1/tcp/%d", port)),
Identity(priv),
Muxer(DefaultMuxer()),
DefaultTransports,
DefaultMuxers,
DefaultSecurity,
NATPortMap(),
}
......
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.
func EnableRelay(options ...circuit.RelayOpt) Option {
return func(cfg *Config) error {
cfg.Relay = true
cfg.RelayOpts = options
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
}
}
......@@ -8,7 +8,7 @@ import (
bhost "github.com/libp2p/go-libp2p/p2p/host/basic"
host "github.com/libp2p/go-libp2p-host"
netutil "github.com/libp2p/go-libp2p-netutil"
swarmt "github.com/libp2p/go-libp2p-swarm/testing"
pstore "github.com/libp2p/go-libp2p-peerstore"
)
......@@ -28,8 +28,8 @@ func TestMdnsDiscovery(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
a := bhost.New(netutil.GenSwarmNetwork(t, ctx))
b := bhost.New(netutil.GenSwarmNetwork(t, ctx))
a := bhost.New(swarmt.GenSwarm(t, ctx))
b := bhost.New(swarmt.GenSwarm(t, ctx))
sa, err := NewMdnsService(ctx, a, time.Second, "someTag")
if err != nil {
......
......@@ -9,10 +9,8 @@ import (
logging "github.com/ipfs/go-log"
goprocess "github.com/jbenet/goprocess"
circuit "github.com/libp2p/go-libp2p-circuit"
goprocessctx "github.com/jbenet/goprocess/context"
ifconnmgr "github.com/libp2p/go-libp2p-interface-connmgr"
metrics "github.com/libp2p/go-libp2p-metrics"
mstream "github.com/libp2p/go-libp2p-metrics/stream"
inet "github.com/libp2p/go-libp2p-net"
peer "github.com/libp2p/go-libp2p-peer"
pstore "github.com/libp2p/go-libp2p-peerstore"
......@@ -67,8 +65,6 @@ type BasicHost struct {
negtimeout time.Duration
proc goprocess.Process
bwc metrics.Reporter
}
// HostOpts holds options that can be passed to NewHost in order to
......@@ -97,25 +93,14 @@ type HostOpts struct {
// NATManager takes care of setting NAT port mappings, and discovering external addresses.
// If omitted, this will simply be disabled.
NATManager NATManager
// BandwidthReporter is used for collecting aggregate metrics of the
// bandwidth used by various protocols.
BandwidthReporter metrics.Reporter
NATManager func(inet.Network) NATManager
// ConnManager is a libp2p connection manager
ConnManager ifconnmgr.ConnManager
// Relay indicates whether the host should use circuit relay transport
EnableRelay bool
// RelayOpts are options for the relay transport; only meaningful when Relay=true
RelayOpts []circuit.RelayOpt
}
// 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) {
ctx, cancel := context.WithCancel(ctx)
h := &BasicHost{
network: net,
mux: msmux.NewMultistreamMuxer(),
......@@ -124,11 +109,10 @@ func NewHost(ctx context.Context, net inet.Network, opts *HostOpts) (*BasicHost,
maResolver: madns.DefaultResolver,
}
h.proc = goprocess.WithTeardown(func() error {
h.proc = goprocessctx.WithContextAndTeardown(ctx, func() error {
if h.natmgr != nil {
h.natmgr.Close()
}
cancel()
return h.Network().Close()
})
......@@ -152,18 +136,13 @@ func NewHost(ctx context.Context, net inet.Network, opts *HostOpts) (*BasicHost,
}
if opts.NATManager != nil {
h.natmgr = opts.NATManager
h.natmgr = opts.NATManager(net)
}
if opts.MultiaddrResolver != nil {
h.maResolver = opts.MultiaddrResolver
}
if opts.BandwidthReporter != nil {
h.bwc = opts.BandwidthReporter
h.ids.Reporter = opts.BandwidthReporter
}
if opts.ConnManager == nil {
h.cmgr = &ifconnmgr.NullConnMgr{}
} else {
......@@ -173,20 +152,16 @@ func NewHost(ctx context.Context, net inet.Network, opts *HostOpts) (*BasicHost,
net.SetConnHandler(h.newConnHandler)
net.SetStreamHandler(h.newStreamHandler)
if opts.EnableRelay {
err := circuit.AddRelayTransport(ctx, h, opts.RelayOpts...)
if err != nil {
h.Close()
return nil, err
}
}
return h, nil
}
// New constructs and sets up a new *BasicHost with given Network and options.
// Three options can be passed: NATPortMap, AddrsFactory, and metrics.Reporter.
// 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{}
......@@ -196,10 +171,8 @@ func New(net inet.Network, opts ...interface{}) *BasicHost {
case Option:
switch o {
case NATPortMap:
hostopts.NATManager = newNatManager(net)
hostopts.NATManager = NewNATManager
}
case metrics.Reporter:
hostopts.BandwidthReporter = o
case AddrsFactory:
hostopts.AddrsFactory = AddrsFactory(o)
case ifconnmgr.ConnManager:
......@@ -270,10 +243,6 @@ func (h *BasicHost) newStreamHandler(s inet.Stream) {
}
s.SetProtocol(protocol.ID(protoID))
if h.bwc != nil {
s = mstream.WrapStream(s, h.bwc)
}
log.Debugf("protocol negotiation took %s", took)
go handle(protoID, s)
......@@ -366,10 +335,6 @@ func (h *BasicHost) NewStream(ctx context.Context, p peer.ID, pids ...protocol.I
s.SetProtocol(selpid)
h.Peerstore().AddProtocols(p, selected)
if h.bwc != nil {
s = mstream.WrapStream(s, h.bwc)
}
return s, nil
}
......@@ -403,10 +368,6 @@ func (h *BasicHost) newStream(ctx context.Context, p peer.ID, pid protocol.ID) (
s.SetProtocol(pid)
if h.bwc != nil {
s = mstream.WrapStream(s, h.bwc)
}
lzcon := msmux.NewMSSelect(s, string(pid))
return &streamWrapper{
Stream: s,
......@@ -536,11 +497,6 @@ func (h *BasicHost) Close() error {
return h.proc.Close()
}
// GetBandwidthReporter exposes the Host's bandiwth metrics reporter
func (h *BasicHost) GetBandwidthReporter() metrics.Reporter {
return h.bwc
}
type streamWrapper struct {
inet.Stream
rw io.ReadWriter
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment