libp2p.go 5.37 KB
Newer Older
1
2
3
4
5
package libp2p

import (
	"context"
	"crypto/rand"
Jeromy's avatar
Jeromy committed
6
	"fmt"
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

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

Jeromy's avatar
Jeromy committed
24
// Config describes a set of settings for a libp2p node
25
type Config struct {
Jeromy's avatar
Jeromy committed
26
27
28
29
30
31
32
33
	Transports   []transport.Transport
	Muxer        mux.Transport
	ListenAddrs  []ma.Multiaddr
	PeerKey      crypto.PrivKey
	Peerstore    pstore.Peerstore
	Protector    pnet.Protector
	Reporter     metrics.Reporter
	DisableSecio bool
34
	EnableNAT    bool
35
36
}

Jeromy's avatar
Jeromy committed
37
38
type Option func(cfg *Config) error

Jeromy's avatar
Jeromy committed
39
func Transports(tpts ...transport.Transport) Option {
Jeromy's avatar
Jeromy committed
40
	return func(cfg *Config) error {
Jeromy's avatar
Jeromy committed
41
		cfg.Transports = append(cfg.Transports, tpts...)
Jeromy's avatar
Jeromy committed
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
		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
	}
}

Jeromy's avatar
Jeromy committed
59
func ListenAddrs(addrs ...ma.Multiaddr) Option {
Jeromy's avatar
Jeromy committed
60
61
62
63
64
65
	return func(cfg *Config) error {
		cfg.ListenAddrs = append(cfg.ListenAddrs, addrs...)
		return nil
	}
}

Jeromy's avatar
Jeromy committed
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
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
	}
Jeromy's avatar
Jeromy committed
90
91
}

Jeromy's avatar
Jeromy committed
92
93
94
95
func NoEncryption() Option {
	return TransportEncryption(EncPlaintext)
}

96
97
98
99
100
101
102
func NATPortMap() Option {
	return func(cfg *Config) error {
		cfg.EnableNAT = true
		return nil
	}
}

Jeromy's avatar
Jeromy committed
103
func Muxer(m mux.Transport) Option {
Jeromy's avatar
Jeromy committed
104
	return func(cfg *Config) error {
Jeromy's avatar
Jeromy committed
105
106
107
108
		if cfg.Muxer != nil {
			return fmt.Errorf("cannot specify multiple muxer options")
		}

Jeromy's avatar
Jeromy committed
109
110
111
112
113
		cfg.Muxer = m
		return nil
	}
}

Jeromy's avatar
Jeromy committed
114
func Peerstore(ps pstore.Peerstore) Option {
Jeromy's avatar
Jeromy committed
115
	return func(cfg *Config) error {
Jeromy's avatar
Jeromy committed
116
117
118
119
		if cfg.Peerstore != nil {
			return fmt.Errorf("cannot specify multiple peerstore options")
		}

Jeromy's avatar
Jeromy committed
120
121
122
123
124
		cfg.Peerstore = ps
		return nil
	}
}

Jeromy's avatar
Jeromy committed
125
func PrivateNetwork(prot pnet.Protector) Option {
Jeromy's avatar
Jeromy committed
126
	return func(cfg *Config) error {
Jeromy's avatar
Jeromy committed
127
128
129
130
		if cfg.Protector != nil {
			return fmt.Errorf("cannot specify multiple private network options")
		}

Jeromy's avatar
Jeromy committed
131
132
133
134
135
		cfg.Protector = prot
		return nil
	}
}

Jeromy's avatar
Jeromy committed
136
func BandwidthReporter(rep metrics.Reporter) Option {
Jeromy's avatar
Jeromy committed
137
	return func(cfg *Config) error {
Jeromy's avatar
Jeromy committed
138
139
140
141
		if cfg.Reporter != nil {
			return fmt.Errorf("cannot specify multiple bandwidth reporter options")
		}

Jeromy's avatar
Jeromy committed
142
143
144
145
146
		cfg.Reporter = rep
		return nil
	}
}

Jeromy's avatar
Jeromy committed
147
148
149
150
func Identity(sk crypto.PrivKey) Option {
	return func(cfg *Config) error {
		if cfg.PeerKey != nil {
			return fmt.Errorf("cannot specify multiple identities")
Jeromy's avatar
Jeromy committed
151
152
153
154
155
		}

		cfg.PeerKey = sk
		return nil
	}
Jeromy's avatar
Jeromy committed
156
157
}

Jeromy's avatar
Jeromy committed
158
159
160
161
162
163
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
		}
164
165
	}

Jeromy's avatar
Jeromy committed
166
167
168
169
	return newWithCfg(ctx, &cfg)
}

func newWithCfg(ctx context.Context, cfg *Config) (host.Host, error) {
Jeromy's avatar
Jeromy committed
170
	// If no key was given, generate a random 2048 bit RSA key
171
172
173
174
175
176
177
178
179
180
181
182
183
184
	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
	}

Jeromy's avatar
Jeromy committed
185
	// Create a new blank peerstore if none was passed in
186
187
188
189
190
	ps := cfg.Peerstore
	if ps == nil {
		ps = pstore.NewPeerstore()
	}

191
192
193
194
195
196
	// Set default muxer if none was passed in
	muxer := cfg.Muxer
	if muxer == nil {
		muxer = DefaultMuxer()
	}

Jeromy's avatar
Jeromy committed
197
198
199
200
201
	// 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())
	}
202

203
	swrm, err := swarm.NewSwarmWithProtector(ctx, cfg.ListenAddrs, pid, ps, cfg.Protector, muxer, cfg.Reporter)
204
205
206
207
208
209
	if err != nil {
		return nil, err
	}

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

210
211
212
213
214
215
216
	hostOpts := &bhost.HostOpts{}

	if cfg.EnableNAT {
		hostOpts.NATManager = bhost.NewNATManager(netw)
	}

	return bhost.NewHost(ctx, netw, hostOpts)
217
218
219
220
221
}

func DefaultMuxer() mux.Transport {
	// Set up stream multiplexer
	tpt := msmux.NewBlankTransport()
Jeromy's avatar
Jeromy committed
222
223

	// By default, support yamux and multiplex
224
225
	tpt.AddTransport("/yamux/1.0.0", yamux.DefaultTransport)
	tpt.AddTransport("/mplex/6.3.0", mplex.DefaultTransport)
Jeromy's avatar
Jeromy committed
226

227
228
229
	return tpt
}

Jeromy's avatar
Jeromy committed
230
func Defaults(cfg *Config) error {
Jeromy's avatar
Jeromy committed
231
232
	// Create a multiaddress that listens on a random port on all interfaces
	addr, err := ma.NewMultiaddr("/ip4/0.0.0.0/tcp/0")
233
	if err != nil {
Jeromy's avatar
Jeromy committed
234
		return err
235
236
	}

Jeromy's avatar
Jeromy committed
237
238
239
240
	cfg.ListenAddrs = []ma.Multiaddr{addr}
	cfg.Peerstore = pstore.NewPeerstore()
	cfg.Muxer = DefaultMuxer()
	return nil
241
}