mirror of https://github.com/libp2p/go-libp2p.git
Browse Source
Also, make the libp2p constructor fully useful. There should now be no need to manually construct a swarm/host.pull/299/head
Steven Allen
7 years ago
28 changed files with 1335 additions and 511 deletions
@ -0,0 +1,176 @@ |
|||
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 |
|||
} |
@ -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 |
|||
} |
@ -0,0 +1,62 @@ |
|||
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 |
|||
} |
@ -0,0 +1,54 @@ |
|||
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) |
|||
} |
|||
} |
|||
} |
@ -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("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 |
|||
} |
@ -0,0 +1,142 @@ |
|||
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") |
|||
} |
|||
} |
@ -0,0 +1,76 @@ |
|||
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 |
|||
} |
@ -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 |
|||
} |
@ -0,0 +1,109 @@ |
|||
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 |
|||
} |
@ -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) |
|||
} |
@ -0,0 +1,243 @@ |
|||
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 |
|||
} |
|||
} |
Loading…
Reference in new issue