Skip to content

Commit

Permalink
refactor for transport changes
Browse files Browse the repository at this point in the history
Also, make the libp2p constructor fully useful. There should now be no need to
manually construct a swarm/host.
  • Loading branch information
Stebalien committed Jun 5, 2018
1 parent 4b33a80 commit 41c6834
Show file tree
Hide file tree
Showing 28 changed files with 1,335 additions and 511 deletions.
176 changes: 176 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
@@ -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
}
62 changes: 62 additions & 0 deletions config/constructor_types.go
Original file line number Diff line number Diff line change
@@ -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
}
62 changes: 62 additions & 0 deletions config/muxer.go
Original file line number Diff line number Diff line change
@@ -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
}
54 changes: 54 additions & 0 deletions config/muxer_test.go
Original file line number Diff line number Diff line change
@@ -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)
}
}
}
Loading

0 comments on commit 41c6834

Please sign in to comment.