diff --git a/examples/echo/main.go b/examples/echo/main.go index cc37453da3..412df18fc1 100644 --- a/examples/echo/main.go +++ b/examples/echo/main.go @@ -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 @@ -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 { diff --git a/examples/libp2p-host/host.go b/examples/libp2p-host/host.go index 9c028206ec..793cdf4fd8 100644 --- a/examples/libp2p-host/host.go +++ b/examples/libp2p-host/host.go @@ -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()) } diff --git a/libp2p.go b/libp2p.go new file mode 100644 index 0000000000..837f9fddf0 --- /dev/null +++ b/libp2p.go @@ -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 + 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 +} diff --git a/package.json b/package.json index 3f49be09af..26a4346730 100644 --- a/package.json +++ b/package.json @@ -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",