Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

first hack at a nice libp2p constructor #241

Merged
merged 6 commits into from
Jan 7, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 9 additions & 42 deletions examples/echo/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,14 @@ import (
mrand "math/rand"

golog "github.com/ipfs/go-log"
libp2p "github.com/libp2p/go-libp2p"
crypto "github.com/libp2p/go-libp2p-crypto"
host "github.com/libp2p/go-libp2p-host"
net "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"
bhost "github.com/libp2p/go-libp2p/p2p/host/basic"
ma "github.com/multiformats/go-multiaddr"
gologging "github.com/whyrusleeping/go-logging"
msmux "github.com/whyrusleeping/go-smux-multistream"
yamux "github.com/whyrusleeping/go-smux-yamux"
)

// makeBasicHost creates a LibP2P host with a random peer ID listening on the
Expand All @@ -41,61 +38,31 @@ func makeBasicHost(listenPort int, secio bool, randseed int64) (host.Host, error

// Generate a key pair for this host. We will use it at least
// to obtain a valid host ID.
priv, pub, err := crypto.GenerateKeyPairWithReader(crypto.RSA, 2048, r)
priv, _, err := crypto.GenerateKeyPairWithReader(crypto.RSA, 2048, r)
if err != nil {
return nil, err
}

// Obtain Peer ID from public key
pid, err := peer.IDFromPublicKey(pub)
if err != nil {
return nil, err
opts := []libp2p.Option{
libp2p.ListenAddrStrings(fmt.Sprintf("/ip4/127.0.0.1/tcp/%d", listenPort)),
libp2p.Identity(priv),
}

// Create a multiaddress
addr, err := ma.NewMultiaddr(fmt.Sprintf("/ip4/127.0.0.1/tcp/%d", listenPort))

if err != nil {
return nil, err
if !secio {
opts = append(opts, libp2p.NoEncryption())
}

// Create a peerstore
ps := pstore.NewPeerstore()

// If using secio, we add the keys to the peerstore
// for this peer ID.
if secio {
ps.AddPrivKey(pid, priv)
ps.AddPubKey(pid, pub)
}

// Set up stream multiplexer
tpt := msmux.NewBlankTransport()
tpt.AddTransport("/yamux/1.0.0", yamux.DefaultTransport)

// Create swarm (implements libP2P Network)
swrm, err := swarm.NewSwarmWithProtector(
context.Background(),
[]ma.Multiaddr{addr},
pid,
ps,
nil,
tpt,
nil,
)
basicHost, err := libp2p.New(context.Background(), opts...)
if err != nil {
return nil, err
}

netw := (*swarm.Network)(swrm)

basicHost := bhost.New(netw)

// Build host multiaddress
hostAddr, _ := ma.NewMultiaddr(fmt.Sprintf("/ipfs/%s", basicHost.ID().Pretty()))

// Now we can build a full multiaddress to reach this host
// by encapsulating both addresses:
addr := basicHost.Addrs()[0]
fullAddr := addr.Encapsulate(hostAddr)
log.Printf("I am %s\n", fullAddr)
if secio {
Expand Down
44 changes: 20 additions & 24 deletions examples/libp2p-host/host.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,47 +5,43 @@ import (
"crypto/rand"
"fmt"

libp2p "github.com/libp2p/go-libp2p"
crypto "github.com/libp2p/go-libp2p-crypto"
peer "github.com/libp2p/go-libp2p-peer"
pstore "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"
)

func main() {
// Generate an identity keypair using go's cryptographic randomness source
priv, pub, err := crypto.GenerateEd25519Key(rand.Reader)
if err != nil {
panic(err)
}
// The context governs the lifetime of the libp2p node
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

// A peers ID is the hash of its public key
pid, err := peer.IDFromPublicKey(pub)
// To construct a simple host with all the default settings, just use `New`
h, err := libp2p.New(ctx)
if err != nil {
panic(err)
}

// We've created the identity, now we need to store it.
// A peerstore holds information about peers, including your own
ps := pstore.NewPeerstore()
ps.AddPrivKey(pid, priv)
ps.AddPubKey(pid, pub)
fmt.Printf("Hello World, my hosts ID is %s\n", h.ID())

// If you want more control over the configuration, you can specify some
// options to the constructor

maddr, err := ma.NewMultiaddr("/ip4/0.0.0.0/tcp/9000")
// Set your own keypair
priv, _, err := crypto.GenerateEd25519Key(rand.Reader)
if err != nil {
panic(err)
}

// Make a context to govern the lifespan of the swarm
ctx := context.Background()
h2, err := libp2p.New(ctx,
// Use your own created keypair
libp2p.Identity(priv),

// Put all this together
netw, err := swarm.NewNetwork(ctx, []ma.Multiaddr{maddr}, pid, ps, nil)
// Set your own listen address
// The config takes an array of addresses, specify as many as you want.
libp2p.ListenAddrStrings("/ip4/0.0.0.0/tcp/9000"),
)
if err != nil {
panic(err)
}

myhost := bhost.New(netw)
fmt.Printf("Hello World, my hosts ID is %s\n", myhost.ID())
fmt.Printf("Hello World, my second hosts ID is %s\n", h2.ID())
}
221 changes: 221 additions & 0 deletions libp2p.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
package libp2p

import (
"context"
"crypto/rand"
"fmt"

crypto "github.com/libp2p/go-libp2p-crypto"
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
}

type Option func(cfg *Config) error

func Transports(tpts ...transport.Transport) Option {
return func(cfg *Config) error {
cfg.Transports = append(cfg.Transports, tpts...)
return nil
}
}

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
}
}

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 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
}
}

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)
}

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()
}

// 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, cfg.Muxer, cfg.Reporter)
if err != nil {
return nil, err
}

netw := (*swarm.Network)(swrm)

return bhost.New(netw), nil
}

func DefaultMuxer() mux.Transport {
// Set up stream multiplexer
tpt := msmux.NewBlankTransport()

// By default, support yamux and multiplex
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It strikes me that here the supported transports are slightly different than here: https://github.com/libp2p/go-libp2p-swarm/blob/master/swarm.go

I thought that swarm.NewNetworkWithProtector would be the default way of doing things so any updates to that should happen in swarm?

At first sight, it seems that this function is just re-doing what PSTransport provides:

// PSTransport is the default peerstream transport that will be used by
// any libp2p swarms.
var PSTransport pst.Transport

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, I'm actually thinking the best path forward here is to remove that logic from the swarm code, and make this place be the default. The reason being that swarm is much deeper in the dependency tree, and making it depend on extra packages makes it harder to update those packages.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That sounds good to me 👍

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
}
6 changes: 6 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,12 @@
"hash": "QmSAJm4QdTJ3EGF2cvgNcQyXTEbxqWSW1x4kCVV1aJQUQr",
"name": "go-libp2p-interface-connmgr",
"version": "0.0.4"
},
{
"author": "whyrusleeping",
"hash": "QmREBy6TSjLQMtYFhjf97cypsUTzBagcwamWocKHFCTb1e",
"name": "go-smux-multiplex",
"version": "3.0.4"
}
],
"gxVersion": "0.4.0",
Expand Down