mock_net.go 8.29 KB
Newer Older
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
1
2
3
package mocknet

import (
Jeromy's avatar
Jeromy committed
4
	"context"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
5
	"fmt"
6
	"net"
7
	"sort"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
8
9
	"sync"

Jeromy's avatar
Jeromy committed
10
	host "github.com/libp2p/go-libp2p-host"
11
	bhost "github.com/libp2p/go-libp2p/p2p/host/basic"
12

Jeromy's avatar
Jeromy committed
13
14
	"github.com/jbenet/goprocess"
	goprocessctx "github.com/jbenet/goprocess/context"
Jeromy's avatar
Jeromy committed
15
	ic "github.com/libp2p/go-libp2p-crypto"
Jeromy's avatar
Jeromy committed
16
	inet "github.com/libp2p/go-libp2p-net"
Jeromy's avatar
Jeromy committed
17
	p2putil "github.com/libp2p/go-libp2p-netutil"
Jeromy's avatar
Jeromy committed
18
19
	peer "github.com/libp2p/go-libp2p-peer"
	pstore "github.com/libp2p/go-libp2p-peerstore"
Steven Allen's avatar
Steven Allen committed
20
	pstoremem "github.com/libp2p/go-libp2p-peerstore/pstoremem"
Jeromy's avatar
Jeromy committed
21
	ma "github.com/multiformats/go-multiaddr"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
22
23
)

24
25
26
27
// IP6 range that gets blackholed (in case our traffic ever makes it out onto
// the internet).
var blackholeIP6 = net.ParseIP("100::")

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
28
29
// mocknet implements mocknet.Mocknet
type mocknet struct {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
30
31
	nets  map[peer.ID]*peernet
	hosts map[peer.ID]*bhost.BasicHost
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
32
33
34
35
36
37
38
39
40

	// links make it possible to connect two peers.
	// think of links as the physical medium.
	// usually only one, but there could be multiple
	// **links are shared between peers**
	links map[peer.ID]map[peer.ID]map[*link]struct{}

	linkDefaults LinkOptions

41
	proc goprocess.Process // for Context closing
42
	ctx  context.Context
Jeromy's avatar
Jeromy committed
43
	sync.Mutex
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
44
45
46
47
48
}

func New(ctx context.Context) Mocknet {
	return &mocknet{
		nets:  map[peer.ID]*peernet{},
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
49
		hosts: map[peer.ID]*bhost.BasicHost{},
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
50
		links: map[peer.ID]map[peer.ID]map[*link]struct{}{},
51
		proc:  goprocessctx.WithContext(ctx),
52
		ctx:   ctx,
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
53
54
55
	}
}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
56
func (mn *mocknet) GenPeer() (host.Host, error) {
57
	sk, err := p2putil.RandTestBogusPrivateKey()
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
58
59
60
	if err != nil {
		return nil, err
	}
61
62
63
64
65
66
67
68
69
70
71
72
73
74
	id, err := peer.IDFromPrivateKey(sk)
	if err != nil {
		return nil, err
	}
	suffix := id
	if len(id) > 8 {
		suffix = id[len(id)-8:]
	}
	ip := append(net.IP{}, blackholeIP6...)
	copy(ip[net.IPv6len-len(suffix):], suffix)
	a, err := ma.NewMultiaddr(fmt.Sprintf("/ip6/%s/tcp/4242", ip))
	if err != nil {
		return nil, fmt.Errorf("failed to create test multiaddr: %s", err)
	}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
75

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
76
	h, err := mn.AddPeer(sk, a)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
77
78
79
80
	if err != nil {
		return nil, err
	}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
81
	return h, nil
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
82
83
}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
84
func (mn *mocknet) AddPeer(k ic.PrivKey, a ma.Multiaddr) (host.Host, error) {
85
86
87
88
89
	p, err := peer.IDFromPublicKey(k.GetPublic())
	if err != nil {
		return nil, err
	}

Steven Allen's avatar
Steven Allen committed
90
	ps := pstoremem.NewPeerstore()
Jeromy's avatar
Jeromy committed
91
	ps.AddAddr(p, a, pstore.PermanentAddrTTL)
92
93
94
95
96
97
	ps.AddPrivKey(p, k)
	ps.AddPubKey(p, k.GetPublic())

	return mn.AddPeerWithPeerstore(p, ps)
}

Jeromy's avatar
Jeromy committed
98
func (mn *mocknet) AddPeerWithPeerstore(p peer.ID, ps pstore.Peerstore) (host.Host, error) {
99
	n, err := newPeernet(mn.ctx, mn, p, ps)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
100
101
102
103
	if err != nil {
		return nil, err
	}

104
105
106
	opts := &bhost.HostOpts{
		NegotiationTimeout: -1,
	}
107

108
	h, err := bhost.NewHost(mn.ctx, n, opts)
109
110
111
	if err != nil {
		return nil, err
	}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
112

113
	mn.proc.AddChild(n.proc)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
114
115
116

	mn.Lock()
	mn.nets[n.peer] = n
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
117
	mn.hosts[n.peer] = h
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
118
	mn.Unlock()
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
119
	return h, nil
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
120
121
122
}

func (mn *mocknet) Peers() []peer.ID {
Jeromy's avatar
Jeromy committed
123
124
	mn.Lock()
	defer mn.Unlock()
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
125
126
127
128
129

	cp := make([]peer.ID, 0, len(mn.nets))
	for _, n := range mn.nets {
		cp = append(cp, n.peer)
	}
130
	sort.Sort(peer.IDSlice(cp))
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
131
132
133
	return cp
}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
134
func (mn *mocknet) Host(pid peer.ID) host.Host {
Jeromy's avatar
Jeromy committed
135
	mn.Lock()
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
136
	host := mn.hosts[pid]
Jeromy's avatar
Jeromy committed
137
	mn.Unlock()
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
138
139
140
	return host
}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
141
func (mn *mocknet) Net(pid peer.ID) inet.Network {
Jeromy's avatar
Jeromy committed
142
	mn.Lock()
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
143
	n := mn.nets[pid]
Jeromy's avatar
Jeromy committed
144
	mn.Unlock()
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
145
146
147
148
	return n
}

func (mn *mocknet) Hosts() []host.Host {
Jeromy's avatar
Jeromy committed
149
150
	mn.Lock()
	defer mn.Unlock()
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
151

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
152
153
154
	cp := make([]host.Host, 0, len(mn.hosts))
	for _, h := range mn.hosts {
		cp = append(cp, h)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
155
	}
156
157

	sort.Sort(hostSlice(cp))
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
158
	return cp
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
159
160
161
}

func (mn *mocknet) Nets() []inet.Network {
Jeromy's avatar
Jeromy committed
162
163
	mn.Lock()
	defer mn.Unlock()
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
164
165
166
167
168

	cp := make([]inet.Network, 0, len(mn.nets))
	for _, n := range mn.nets {
		cp = append(cp, n)
	}
169
	sort.Sort(netSlice(cp))
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
170
171
172
173
174
175
	return cp
}

// Links returns a copy of the internal link state map.
// (wow, much map. so data structure. how compose. ahhh pointer)
func (mn *mocknet) Links() LinkMap {
Jeromy's avatar
Jeromy committed
176
177
	mn.Lock()
	defer mn.Unlock()
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206

	links := map[string]map[string]map[Link]struct{}{}
	for p1, lm := range mn.links {
		sp1 := string(p1)
		links[sp1] = map[string]map[Link]struct{}{}
		for p2, ls := range lm {
			sp2 := string(p2)
			links[sp1][sp2] = map[Link]struct{}{}
			for l := range ls {
				links[sp1][sp2][l] = struct{}{}
			}
		}
	}
	return links
}

func (mn *mocknet) LinkAll() error {
	nets := mn.Nets()
	for _, n1 := range nets {
		for _, n2 := range nets {
			if _, err := mn.LinkNets(n1, n2); err != nil {
				return err
			}
		}
	}
	return nil
}

func (mn *mocknet) LinkPeers(p1, p2 peer.ID) (Link, error) {
Jeromy's avatar
Jeromy committed
207
	mn.Lock()
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
208
209
	n1 := mn.nets[p1]
	n2 := mn.nets[p2]
Jeromy's avatar
Jeromy committed
210
	mn.Unlock()
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238

	if n1 == nil {
		return nil, fmt.Errorf("network for p1 not in mocknet")
	}

	if n2 == nil {
		return nil, fmt.Errorf("network for p2 not in mocknet")
	}

	return mn.LinkNets(n1, n2)
}

func (mn *mocknet) validate(n inet.Network) (*peernet, error) {
	// WARNING: assumes locks acquired

	nr, ok := n.(*peernet)
	if !ok {
		return nil, fmt.Errorf("Network not supported (use mock package nets only)")
	}

	if _, found := mn.nets[nr.peer]; !found {
		return nil, fmt.Errorf("Network not on mocknet. is it from another mocknet?")
	}

	return nr, nil
}

func (mn *mocknet) LinkNets(n1, n2 inet.Network) (Link, error) {
Jeromy's avatar
Jeromy committed
239
	mn.Lock()
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
240
241
242
	n1r, err1 := mn.validate(n1)
	n2r, err2 := mn.validate(n2)
	ld := mn.linkDefaults
Jeromy's avatar
Jeromy committed
243
	mn.Unlock()
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287

	if err1 != nil {
		return nil, err1
	}
	if err2 != nil {
		return nil, err2
	}

	l := newLink(mn, ld)
	l.nets = append(l.nets, n1r, n2r)
	mn.addLink(l)
	return l, nil
}

func (mn *mocknet) Unlink(l2 Link) error {

	l, ok := l2.(*link)
	if !ok {
		return fmt.Errorf("only links from mocknet are supported")
	}

	mn.removeLink(l)
	return nil
}

func (mn *mocknet) UnlinkPeers(p1, p2 peer.ID) error {
	ls := mn.LinksBetweenPeers(p1, p2)
	if ls == nil {
		return fmt.Errorf("no link between p1 and p2")
	}

	for _, l := range ls {
		if err := mn.Unlink(l); err != nil {
			return err
		}
	}
	return nil
}

func (mn *mocknet) UnlinkNets(n1, n2 inet.Network) error {
	return mn.UnlinkPeers(n1.LocalPeer(), n2.LocalPeer())
}

// get from the links map. and lazily contruct.
Jeromy's avatar
Jeromy committed
288
func (mn *mocknet) linksMapGet(p1, p2 peer.ID) map[*link]struct{} {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
289
290
291
292
293
294
295
296
297
298
299
300
301
302

	l1, found := mn.links[p1]
	if !found {
		mn.links[p1] = map[peer.ID]map[*link]struct{}{}
		l1 = mn.links[p1] // so we make sure it's there.
	}

	l2, found := l1[p2]
	if !found {
		m := map[*link]struct{}{}
		l1[p2] = m
		l2 = l1[p2]
	}

Jeromy's avatar
Jeromy committed
303
	return l2
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
304
305
306
307
308
309
310
}

func (mn *mocknet) addLink(l *link) {
	mn.Lock()
	defer mn.Unlock()

	n1, n2 := l.nets[0], l.nets[1]
Jeromy's avatar
Jeromy committed
311
312
	mn.linksMapGet(n1.peer, n2.peer)[l] = struct{}{}
	mn.linksMapGet(n2.peer, n1.peer)[l] = struct{}{}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
313
314
315
316
317
318
319
}

func (mn *mocknet) removeLink(l *link) {
	mn.Lock()
	defer mn.Unlock()

	n1, n2 := l.nets[0], l.nets[1]
Jeromy's avatar
Jeromy committed
320
321
	delete(mn.linksMapGet(n1.peer, n2.peer), l)
	delete(mn.linksMapGet(n2.peer, n1.peer), l)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
322
323
}

rht's avatar
rht committed
324
func (mn *mocknet) ConnectAllButSelf() error {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
325
326
327
328
329
330
331
	nets := mn.Nets()
	for _, n1 := range nets {
		for _, n2 := range nets {
			if n1 == n2 {
				continue
			}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
332
			if _, err := mn.ConnectNets(n1, n2); err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
333
334
335
336
337
338
339
				return err
			}
		}
	}
	return nil
}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
340
func (mn *mocknet) ConnectPeers(a, b peer.ID) (inet.Conn, error) {
341
	return mn.Net(a).DialPeer(mn.ctx, b)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
342
343
}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
344
func (mn *mocknet) ConnectNets(a, b inet.Network) (inet.Conn, error) {
345
	return a.DialPeer(mn.ctx, b.LocalPeer())
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
346
347
348
349
350
351
352
353
354
355
356
}

func (mn *mocknet) DisconnectPeers(p1, p2 peer.ID) error {
	return mn.Net(p1).ClosePeer(p2)
}

func (mn *mocknet) DisconnectNets(n1, n2 inet.Network) error {
	return n1.ClosePeer(n2.LocalPeer())
}

func (mn *mocknet) LinksBetweenPeers(p1, p2 peer.ID) []Link {
Jeromy's avatar
Jeromy committed
357
358
	mn.Lock()
	defer mn.Unlock()
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
359

Jeromy's avatar
Jeromy committed
360
	ls2 := mn.linksMapGet(p1, p2)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
	cp := make([]Link, 0, len(ls2))
	for l := range ls2 {
		cp = append(cp, l)
	}
	return cp
}

func (mn *mocknet) LinksBetweenNets(n1, n2 inet.Network) []Link {
	return mn.LinksBetweenPeers(n1.LocalPeer(), n2.LocalPeer())
}

func (mn *mocknet) SetLinkDefaults(o LinkOptions) {
	mn.Lock()
	mn.linkDefaults = o
	mn.Unlock()
}

func (mn *mocknet) LinkDefaults() LinkOptions {
Jeromy's avatar
Jeromy committed
379
380
	mn.Lock()
	defer mn.Unlock()
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
381
382
	return mn.linkDefaults
}
383
384
385
386
387
388
389
390
391
392
393
394
395
396

// netSlice for sorting by peer
type netSlice []inet.Network

func (es netSlice) Len() int           { return len(es) }
func (es netSlice) Swap(i, j int)      { es[i], es[j] = es[j], es[i] }
func (es netSlice) Less(i, j int) bool { return string(es[i].LocalPeer()) < string(es[j].LocalPeer()) }

// hostSlice for sorting by peer
type hostSlice []host.Host

func (es hostSlice) Len() int           { return len(es) }
func (es hostSlice) Swap(i, j int)      { es[i], es[j] = es[j], es[i] }
func (es hostSlice) Less(i, j int) bool { return string(es[i].ID()) < string(es[j].ID()) }